从默认接口方法记录
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;}
}
}
向所有 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;} } }