And again about laziness

Good afternoon!
 
 
As you know, "laziness is the engine of progress", the most useful quality of a programmer, thanks to it appeared a lot of wonderful frameworks and so on, and so forth. But today I want to write not about human laziness.
 
 
A couple of weeks ago I caught my eye article about a rough draft of a feature, a new lazy modifier for final fields. And of course, the initialization of loggers is shown as the most obvious example, when this feature would come in handy. No, no one argues, of course the loggers are overhead, create them during the start, then keep in memory. Br. But can not you write elegant crutch solution on the good old Java?
 
org.slf4j.Logger (I chose slf4j, but all this is true for any other logging framework), we encapsulate the real logger, initialize it when some interface method is invoked. The Proxy pattern plus the Factory Method. It's simple, everything works.
 
 
public class LazyLogger implements Logger {
private Logger realLogger;
private Class
, clazz) {
return new LazyLogger (clazz);
}
private Logger getRealLogger () {
if (realLogger == null)
realLogger = LoggerFactory.getLogger (this.clazz);
return realLogger;
}
@Override
public void trace (String msg) {
getRealLogger (). trace (msg);
}
@Override
public void debug (String msg) {
getRealLogger (). debug (msg);
}
:
:
and

 
But wait, at the interface org.slf4j.Logger about 40 methods, do I have to implement them all with my hands? And getRealLogger () does not look like Thread Safe. So it's no good, let's think further.
 
 

Developing the topic


 
As an option, there are Lombok with the annotation @Delegate .
 
 
@AllArgsConstructor (staticName = "getLogger")
public class LazyLogger implements Logger {
private final static Function
 
@Delegate is responsible for creating the interface methods at compile time. org.slf4j.Logger , inside calls getLogger () + <нужный метод> . At the time of the first call of the method, $ function creates a real logger. $ are added to the fields to hide them from Lombok. It does not generate Getters /Setters, does not create constructors for such fields.
 
 
So, it looks good, but something is missing. And, for sure! Thread Safe. Now we in getLogger () will write double check, moreover with AtomicReference! But wait, Lombok already has @Getter (lazy = true) !.
 
 
@RequiredArgsConstructor (staticName = "getLogger")
public class LazyLoggerThreadSafe implements Logger {
private static final Function
 
What is happening here? The fact is that the Annotation Processor, which is used by Lombok, can go through the source code several times to process the annotations that were generated in the previous step. About it you can read here . During the first pass @Getter (lazy = true) Generates getLogger () with Lazy initialization and annotates it @Delegate . And during the second pass, the methods themselves from @Delegate .
 
 

And for dessert


 
And what if I want Lazy to initialize another object, not a logger? If I need a sort of universal Lazy Factory, where will I transfer only the Supplier that creates the real object? @Delegate already we will not be saved, he needs a concrete class, with a specific set of methods. But it does not matter, we use Dynamic Proxy :
 
 
@AllArgsConstructor (access = AccessLevel.PRIVATE)
public class LazyFactory
{
private Class
interfaceClass;
private Supplier
supplier;
@SuppressWarnings ("unchecked")
private I getLazyObject () {
return (I) Proxy.newProxyInstance (
LazyFactory.class.getClassLoader (),
new Class[]{interfaceClass},
new LazyFactory.DynamicInvocationHandler ());
}
public static
T getLazy (Class
? interfaceClass, Supplier
Supplier) {
return new LazyFactory
(interfaceClass, supplier) .getLazyObject ();
}
private class DynamicInvocationHandler implements InvocationHandler {
@Getter (lazy = true)
private final I internalObject = supplier.get ();
@Override
public Object invoke (Object proxy, Method method, Object[].args) throws Throwable {
return method.invoke (getInternalObject (), args);
}
}
}
Use:
public interface Hello {
void sayHello ();
}
public class LazyHello implements Hello {
public LazyHello () {
System.out.println ("LazyHello under constuction");
}
@Override
public void sayHello () {
System.out.println ("I'm very lazy for saying Hello ");
}
}
private static final Hello lazyObj = LazyFactory.getLazy (Hello.class, LazyHello :: new);
lazyObj.sayHello ();

 
As you can see, there is not much code either, in part thanks to Lombok. A few words, how it works. LazyFactory in a static method returns a dynamic proxy. Inside DynamicInvocationHandler is a "real object", but it will only be created when invoke () is called DynamicInvocationHandler, that is, one of the methods of interface I.
 
For creating a "real object", getInternalObject (), which is generated by
? is responsible. @Getter (lazy = true)
.
 
 
The topic can be developed further, but it is already clear that lazy initialization of anything is simple, concise and easily integrated into the existing code.
 
 
Thanks for attention, all the best!
 
 
References:
 
 
JEP draft: Lazy Static Final Fields
 
Lombok
+ 0 -

Comments 1

Offline
Lawford
Lawford 11 October 2018 13:09
If you can do any one things, what would it be? I think that it's all about the way that it goes on click  to see auassignment help and people need to check it. It'll be great to attend it for sure.

Add comment