@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:
- some evil code may steal your lock (very popular this one, also has an "accidentally" variant)
- all synchronized methods within the same class use the exact same lock, which reduces throughput
- you are (unnecessarily) exposing too much information
@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 {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
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
lazy
attribute 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 yourrun()
method, checked or not, it will be passed to theThread
's unhandled exception handler. Catching a checked exception and wrapping it in some sort ofRuntimeException
is only obscuring the real cause of the issue. - An 'impossible' exception. For example,
new String(someByteArray, "UTF-8");
declares that it can throw anUnsupportedEncodingException
but according to the JVM specification, UTF-8 must always be available. AnUnsupportedEncodingException
here is about as likely as aClassNotFoundError
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:
When annotated with
@SneakyThrows
, the error goes away.
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 RuntimeException
and 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
Post a Comment