Java Lombok Tutorial | Part 3

@Synchronized Annotations

@Synchronized is a safer variant of the synchronized method modifier. Like synchronized, the annotation can be used on static and instance methods only. It operates similarly to the synchronized keyword, but it locks on different objects. The keyword locks on this, but the annotation locks on a field named $lock, which is private.
If the field does not exist, it is created for you. If you annotate a static method, the annotation locks on a static field named $LOCK instead.
If you want, you can create these locks yourself. The $lock and $LOCK fields will of course not be generated if you already created them yourself. You can also choose to lock on another field, by specifying it as parameter to the @Synchronized annotation. In this usage variant, the fields will not be created automatically, and you must explicitly create them yourself, or an error will be emitted.
Locking on this or your own class object can have unfortunate side-effects, as other code not under your control can lock on these objects as well, which can cause race conditions and other nasty threading-related bugs.

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.Synchronized;

public class SynchronizedExample {
   private final Object fooLock = new Object();
@Synchronized
  public int answerToLife() {
    System.out.println("Automaic created non-static Lock field");
    return 42;
  }
@Synchronized
  public static void hello() {
    System.out.println("Automaic created static Lock field");
  }
@Synchronized("fooLock")
public void foo() {
    System.out.println("Manually created Lock field");
  }
}


package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */
public class App 
{
    public static void main( String[] args )
    {

    SynchronizedExample synchronizedExample = new SynchronizedExample();
    
    System.out.println("Printing Object "+synchronizedExample);
    System.out.println("Printing Object "+synchronizedExample.answerToLife());
    synchronizedExample.hello();
synchronizedExample.foo();   

    }
}

Output:

Printing Object tutorial.interviewbubble.LombokSampleApplication.SynchronizedExample@3d4eac69
Automaic created non-static Lock field
Printing Object 42
Automaic created static Lock field
Manually created Lock field


Problem with Synchronized keyword
Whenever a question pops up on SO about Java synchronization, some people are very eager to point out that synchronized(this) should be avoided. Instead, they claim, a lock on a private reference is to be preferred.
Some of the given reasons are:



@Log 

You put the variant of @Log on your class (whichever one applies to the logging system you use); you then have a static final log field, initialized to the name of your class, which you can then use to write log statements.
There are several choices available:
@CommonsLog
Creates private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog(LogExample.class);
@JBossLog
Creates private static final org.jboss.logging.Logger log = org.jboss.logging.Logger.getLogger(LogExample.class);
@Log
Creates private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());
@Log4j
Creates private static final org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(LogExample.class);
@Log4j2
Creates private static final org.apache.logging.log4j.Logger log = org.apache.logging.log4j.LogManager.getLogger(LogExample.class);
@Slf4j
Creates private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);
@XSlf4j
Creates private static final org.slf4j.ext.XLogger log = org.slf4j.ext.XLoggerFactory.getXLogger(LogExample.class);
By default, the topic (or name) of the logger will be the class name of the class annotated with the @Log annotation. This can be customised by specifying the topic parameter. For example: @Slf4j(topic="reporting").

package tutorial.interviewbubble.LombokSampleApplication;

import lombok.extern.slf4j.Slf4j;


/**


* Driver Application to test Lombok Project


*


*/


@Slf4j


public class App


{


public static void main( String[] args )


{






SynchronizedExample synchronizedExample = new SynchronizedExample();





System.out.println("Printing Object "+synchronizedExample);


log.info("Printing Log");


System.out.println("Printing Object "+synchronizedExample.answerToLife());


synchronizedExample.hello();


synchronizedExample.foo();






}






}






Java Code with Lombok Annotation





import lombok.extern.java.Log;

import lombok.extern.slf4j.Slf4j;




@Log

public class LogExample {



public static void main(String... args) {

log.error("Something's wrong here");

}

}




@Slf4j

public class LogExampleOther {



public static void main(String... args) {

log.error("Something else is wrong here");

}

}




@CommonsLog(topic="CounterLog")

public class LogExampleCategory {




public static void main(String... args) {

log.error("Calling the 'CounterLog' with a message");

}


}
Equivalent Java Code

public class LogExample {
private static final java.util.logging.Logger log = java.util.logging.Logger.getLogger(LogExample.class.getName());

public static void main(String... args) {
log.error("Something's wrong here");
}
}

public class LogExampleOther {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExampleOther.class);

public static void main(String... args) {
log.error("Something else is wrong here");
}
}

public class LogExampleCategory {
private static final org.apache.commons.logging.Log log = org.apache.commons.logging.LogFactory.getLog("CounterLog");

public static void main(String... args) {
log.error("Calling the 'CounterLog' with a message");
}
}



@Getter(lazy = true)

Let’s imagine we have a more advanced getter where we want to cache the output from the first method call. For example, we have to do a heavy computation and do not want to trigger that computation on every getter-method call. Exactly for this use-case Lombok privates the lazyattribute in @Getter. Note that @Getter(lazy = true) can be applied on private final fields only.


package tutorial.interviewbubble.LombokSampleApplication;

import lombok.Getter;

public class GetterLazyExample {
@Getter(lazy=true) private final double[] cached = expensive();
 
  private double[] expensive() {
    double[] result = new double[1000000];
    for (int i = 0; i < result.length; i++) {
      result[i] = Math.asin(i);
    }
    return result;
  }
}



val 

You can use val as the type of a local variable declaration instead of actually writing the type. When you do this, the type will be inferred from the initializer expression. The local variable will also be made final. This feature works on local variables and on foreach loops only, not on fields. The initializer expression is required.

val is actually a 'type' of sorts, and exists as a real class in the lombok package. You must import it for val to work (or use lombok.val as the type). The existence of this type on a local variable declaration triggers both the adding of the final keyword as well as copying the type of the initializing expression which overwrites the 'fake' val type.

WARNING: This feature does not currently work in NetBeans.


Note 1. variable should be final

2. it is Applicable only on local variable

package tutorial.interviewbubble.LombokSampleApplication;


import java.util.ArrayList;
import java.util.HashMap;

import lombok.val;

public class ValWithLombok {
public String example() {
    val example = new ArrayList();
    example.add("Hello, World!");
    val foo = example.get(0);
    return foo.toLowerCase();
  }
 
  public void example2() {
    val map = new HashMap();
    map.put(0, "zero");
    map.put(5, "five");
    for (val entry : map.entrySet()) {
      System.out.printf("%d: %s\n", entry.getKey(), entry.getValue());
    }

}
}



package tutorial.interviewbubble.LombokSampleApplication;

/**
 * Driver Application to test Lombok Project
 *
 */

public class App 
{
    public static void main( String[] args )
    {
    ValWithLombok valWithLombok = new ValWithLombok();    
    System.out.println("Printing Object example1\n"+valWithLombok.example());
    System.out.println("Printing Object example2");
    valWithLombok.example2();
    }

}


@SneakyThrows


@SneakyThrows can be used to sneakily throw checked exceptions without actually declaring this in your method's throws clause. This somewhat contentious ability should be used carefully, of course. The code generated by lombok will not ignore, wrap, replace, or otherwise modify the thrown checked exception; it simply fakes out the compiler. On the JVM (class file) level, all exceptions, checked or not, can be thrown regardless of the throws clause of your methods, which is why this works.

Common use cases for when you want to opt out of the checked exception mechanism center around 2 situations:
  • A needlessly strict interface, such as Runnable - whatever exception propagates out of your run() method, checked or not, it will be passed to the Thread's unhandled exception handler. Catching a checked exception and wrapping it in some sort of RuntimeException is only obscuring the real cause of the issue.
  • An 'impossible' exception. For example, new String(someByteArray, "UTF-8"); declares that it can throw an UnsupportedEncodingException but according to the JVM specification, UTF-8 must always be available. An UnsupportedEncodingException here is about as likely as a ClassNotFoundError when you use a String object, and you don't catch those either!


Be aware that it is impossible to catch sneakily thrown checked types directly, as javac will not let you write a catch block for an exception type that no method call in the try body declares as thrown. This problem is not relevant in either of the use cases listed above, so let this serve as a warning that you should not use the @SneakyThrows mechanism without some deliberation!

You can pass any number of exceptions to the @SneakyThrows annotation. If you pass no exceptions, you may throw any exception sneakily.

import lombok.SneakyThrows;

public class SneakyThrowsExample implements Runnable {
  @SneakyThrows(UnsupportedEncodingException.class)
  public String utf8ToString(byte[] bytes) {
    return new String(bytes, "UTF-8");
  }
  
  @SneakyThrows
  public void run() {
    throw new Throwable();
  }
}








@SneakyThrows is probably the Project Lombok annotation with the most detractors, since it is a direct assault on checked exceptions. There is a lot of disagreement with regards to the use of checked exceptions, with a large number of developers holding that they are a failed experiment. These developers will love @SneakyThrows. Those developers on the other side of the checked/unchecked exception fence will most likely view this as hiding potential problems.










Throwing IllegalAccessException would normally generate an "Unhandled exception" error if IllegalAccessException, or some parent class, is not listed in a throws clause:





Screenshot of Eclipse generating an error message regarding unhandled exceptions.



When annotated with @SneakyThrows, the error goes away.


Screenshot of a method annotated with @SneakyThrows and generating no error in Eclipse.

By default, @SneakyThrows will allow any checked exception to be thrown without declaring in the throws clause. This can be limited to a particular set of exceptions by providing an array of throwable classes ( Class) to the value parameter of the annotation.

Lombok annotated code:
@SneakyThrows
public void testSneakyThrows() {
    throw new IllegalAccessException();
}

Equivalent Java source code:
public void testSneakyThrows() {
    try {
        throw new IllegalAccessException();
    } catch (java.lang.Throwable $ex) {
        throw lombok.Lombok.sneakyThrow($ex);
    }
}

A look at the above code and the signature of Lombok.sneakyThrow(Throwable) would lead most to believe that the exception is being wrapped in a RuntimeExceptionand re-thrown, however this is not the case. The sneakyThrow method will never return normally and will instead throw the provided throwable completely unaltered.

Comments