Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Logging from default interface methods

Tags:

Salut to all Java gurus!

Since Java8 we can have default implementations in interfaces (yay!). However problem arises when you want to log from default method.

I have a feeling that it is not wise to call .getLogger() every time I want to log something in a default method.

Yes, one can define static variable in an interface - but that is not a good practice for interfaces anyway + it exposes the logger (must be public).

Solution I have at the moment:

interface WithTimeout<Action> {      default void onTimeout(Action timedOutAction) {         LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);     }      static final class LogHolder {         private static final Logger LOGGER = getLogger(WithTimeout.class);     } } 

LogHolder is still visible to everybody which doesn't really make any sense since it does not provide any methods and it should be internal to the interface.

Does any of you know about better solution? :)

EDIT: I use SLF4J backed by Logback

like image 377
Tomas Zaoral Avatar asked Feb 25 '15 13:02

Tomas Zaoral


People also ask

What are interface methods by default?

Default interface methods enable an API author to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface. The feature enables C# to interoperate with APIs targeting Android (Java) and iOS (Swift), which support similar features.

Can we use logger in interface?

With this attitude it's possible to declare a public static logger in any other class and use it, maybe through a static method, maybe with static import. There's no need for an interface and for a default method in an interface.

How many interface methods are there by default?

Multiple Defaults With default functions in interfaces, there is a possibility that a class is implementing two interfaces with same default methods. The following code explains how this ambiguity can be resolved. First solution is to create an own method that overrides the default implementation.

Can default methods in interface be overridden in Java?

A default method cannot override a method from java.


2 Answers

If you don’t want to expose the class LogHolder to the public, don’t make it a member class of the interface. There is no benefit in making it a member class, you don’t even save typing as you have to qualify the field access with the name of the holder class anyway, regardless of whether it is a member class or a class within the same package:

public interface WithTimeout<Action> {      default void onTimeout(Action timedOutAction) {         LogHolder.LOGGER.info("Action {} time out ignored.", timedOutAction);     } } final class LogHolder { // not public     static final Logger LOGGER = getLogger(WithTimeout.class); } 
like image 160
Holger Avatar answered Sep 16 '22 12:09

Holger


Here you go.

The Logger is private to interface. No one other than this interface and its default methods can access anything inside Test2. And nothing can extend Test2 class.

No one is suggesting you do this ... but it works! And it is a great way to get access to a class logger for the main interface, and possibly a clever way to do something else not entirely crazy.

This is really the same as the LogHolder in the OP question, except that the Test2 class is all private methods and constructor private too, and class is not marked static.

And as an added bonus, it keeps state, statically and per-instance. (Don't do that in a real program, please!)

public class TestRunner {     public static void main(String[] args) {         Test test = new Test() {         };         test.sayHello("Jane");         System.out.println("Again");         test.sayHello("Bob");     } } public interface Test {     default void sayHello(String name) {         Logger log = Test2.log;         Test2 ref = Test2.getMine.apply(this);         int times = ref.getTimes();         for (int i = 0; i < times; i++) {             System.out.println(i + ": Hello " + name);         }         log.info("just said hello {} times :)",times);     }     final class Test2 {         private static final Logger log = LoggerFactory.getLogger(Test.class);         private static final Map lookup = new WeakHashMap();         private static final Function getMine = (obj) -> {             return lookup.computeIfAbsent(obj, (it) -> new Test2());         };         private int calls = 0;         private Test2() {         }         private void setCalls(int calls) {             this.calls = calls;         }         private int getCalls() {return calls;}         private int getTimes() {return ++calls;}     } }
like image 39
The Coordinator Avatar answered Sep 19 '22 12:09

The Coordinator