Java 从默认接口方法进行日志记录

Java 从默认接口方法进行日志记录,java,interface,java-8,Java,Interface,Java 8,向所有Java大师致敬 由于Java8,我们可以在接口中使用默认实现(耶!)。 然而,当您想从默认方法登录时,问题就出现了 我觉得每次我想用默认方法记录某些内容时调用.getLogger()是不明智的 是的,可以在接口中定义静态变量-但这对于接口来说不是一个好的做法+它公开了记录器(必须是公共的) 我目前的解决方案是: interface WithTimeout<Action> { default void onTimeout(Action timedOutAction)

向所有Java大师致敬

由于Java8,我们可以在接口中使用默认实现(耶!)。 然而,当您想从默认方法登录时,问题就出现了


我觉得每次我想用默认方法记录某些内容时调用.getLogger()是不明智的

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

我目前的解决方案是:

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);
    }
}
接口超时{
默认void onTimeout(Action timedOutAction){
LogHolder.LOGGER.info(“忽略动作{}超时。”,timedOutAction);
}
静态最终类日志持有者{
私有静态最终记录器=getLogger(WithTimeout.class);
}
}
LogHolder对每个人都是可见的,这没有任何意义,因为它不提供任何方法,它应该是接口的内部

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


编辑:我使用Logback支持的SLF4J

如果您不想将类
日志持有者
公开,请不要将其作为
接口的成员类。将其设为成员类没有任何好处,您甚至不保存键入,因为您必须使用holder类的名称限定字段访问,无论它是成员类还是同一包中的类:

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);
}
带超时的公共接口{
默认void onTimeout(Action timedOutAction){
LogHolder.LOGGER.info(“忽略动作{}超时。”,timedOutAction);
}
}
最终类日志持有者{//非公共
静态最终记录器=getLogger(WithTimeout.class);
}
给你

记录器对于接口是私有的。除了这个接口及其默认方法之外,没有人可以访问Test2内部的任何内容。并且没有任何东西可以扩展Test2类

没有人建议你这么做。。。但它是有效的! 这是一种访问主界面类记录器的好方法,也可能是一种做其他事情的聪明方法,而不是完全疯狂

这实际上与OP问题中的LogHolder相同,只是Test2类也是所有私有方法和构造函数私有的,并且类没有标记为static

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

公共类测试运行程序{ 公共静态void main(字符串[]args){ 测试=新测试(){ }; test.sayHello(“简”); System.out.println(“再次”); test.sayHello(“Bob”); } } 公共接口测试{ 默认的void sayHello(字符串名称){ 记录器日志=Test2.log; Test2 ref=Test2.getMine.apply(这个); int times=ref.getTimes(); for(int i=0;i{ 返回lookup.computeIfAbsent(obj,(it)->newtest2()); }; 私有int调用=0; 私有测试2(){ } 私有void集合调用(int调用){ this.calls=调用; } private int getCalls(){return calls;} private int getTimes(){return++calls;} }
}

“我觉得每次我想用默认方法记录某些内容时调用.getLogger()是不明智的。”您是否执行了性能测量以支持这种感觉?如果没有,如果不需要,请不要优化性能。公开一个公共
日志持有者
类或一个公共
日志记录器
在我看来没有多大区别。我看不出有任何理由这么做!我不明白如果有多个不同的类正在使用此记录器,这会有什么帮助。@Ray在我关闭此选项之前,我读了一些关于.getLogger的内容。每次调用.getLogger都被认为是反模式的:我没有对性能影响进行任何测量,但我希望以其创建者推荐的方式使用该库。是的,但这并不限制同一程序包的其他成员访问记录器本身。也许我太纯粹了。体面的程序员几乎不会使用不同类别的记录器,我想也不会让它通过代码审查。因为没有办法让
接口的
私有
成员,所以没有办法。但是,即使有一种方法可以使
LogHolder
成为
private
类,也不能阻止其他类调用
getLogger(WithTimeout.class)
自己来获取相同的记录器实例,因此不值得付出努力。将类移出
接口
是很有用的,以避免用实现工件污染API,但是记录器不应该与安全相关。您可以更进一步,将记录器设置为私有的。然后,即使有人可以从包范围访问LogHolder,他们也无法访问该日志,因为它是私有的。但是您的接口可以使用它。@Saint Hill:这只适用于
LogHolder
是嵌套类的情况,但是,它将隐式地
public
,这就是这个问题的全部内容。您只能有一个
private
LOGGER
字段或一个非
public
LogHolder
类。@Holger足够正确。但是该类可以公开显示,并且不允许任何其他人使用它。任何人都能看到其中蕴含着某种神秘。见下面我的答案。现在告诉我这不符合OP的要求:)我不建议这样做,但作为一个LogHolder类,它非常有用。我不是苏尔 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;} } }