从默认接口方法记录

Logging from default interface methods

向所有 Java 大师致敬!

自 Java8 以来,我们可以在接口中使用默认实现(耶!)。 但是,当您想从默认方法登录时会出现问题。

我觉得每次我想用默认方法记录一些东西时调用 .getLogger() 是不明智的。

是的,可以在接口中定义静态变量 - 但无论如何这对接口来说都不是一个好的做法 + 它公开了记录器(必须是 public)。

我目前的解决方案:

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 仍然对每个人可见,这实际上没有任何意义,因为它不提供任何方法并且它应该在接口内部。

你们知道更好的解决方案吗? :)

编辑:我使用由 Logback

支持的 SLF4J

从 JDK16 开始,您可以将助手 class 隐藏在方法中:

interface WithTimeout<Action> {

    default void onTimeout(Action timedOutAction) {
        logger().info("Action {} time out ignored.", timedOutAction);
    }

    private static Logger logger() {
        final class LogHolder {
            private static final Logger LOGGER = getLogger(WithTimeout.class);
        }
        return LogHolder.LOGGER;
    }
}

自 JDK 9 起,允许接口具有 private 方法。要将其用于惰性初始化,我们需要能够在本地 class 中声明一个 static 字段,这是自 JDK 16.

以来允许的。

对于较旧的 Java 版本,如果您不想将 class LogHolder 公开给 public,请不要将其设为成员 [= interface 的 26=]。使其成为成员 class 几乎没有任何好处,您甚至不必节省输入,因为无论如何您都必须使用持有人 class 的名称来限定字段访问权限,无论它是否是成员 class 或同一包内的 class:

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);
}

缺点是同一包内的可见性。当然,一个包中只能有一个名为LogHolder的顶级class。

给你。

Logger 私有 接口。除了此接口及其默认方法之外,没有人可以访问 Test2 中的任何内容。没有什么可以扩展 Test2 class.

没有人建议您这样做...但它确实有效! 这是访问主界面的 class 记录器的好方法,并且可能是一种聪明的方法来做一些不完全疯狂的事情。

这个其实和OP题中的LogHolder是一样的,只是Test2class都是私有方法,构造函数也是私有的,class没有标记为static

作为一个额外的好处,它保持状态,静态和每个实例。 (请不要在真实程序中这样做!)

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;}
    }
}