【问题标题】:Logging from default interface methods从默认接口方法记录
【发布时间】:2015-04-27 13:18:37
【问题描述】:

向所有 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);
    }
}

LogHolder 仍然对所有人可见,这实际上没有任何意义,因为它不提供任何方法,并且应该在接口内部。

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

编辑:我使用由 Logback 支持的 SLF4J

【问题讨论】:

  • “我觉得每次我想用默认方法记录一些东西时调用 .getLogger() 是不明智的。”你是否为了支持这种感觉而进行了性能测量?如果不是,请不要在不需要时优化性能。
  • 在我看来,公开LogHolder 班级或公开Logger LOGGER 并没有太大区别。
  • 我看不出有什么理由耶!
  • 我不明白如果多个不同的类都在使用这个记录器,这会有什么帮助。
  • @Ray 在我关闭此选项之前,我阅读了一些关于 .getLogger 的内容。每次调用 .getLogger 都被认为是反模式:slf4j.org/faq.html#declared_static 我没有对性能影响进行任何测量 - 但我很想以创建者推荐的方式使用该库。

标签: java interface java-8


【解决方案1】:

给你。

Logger 是私有接口。除了这个接口和它的默认方法之外,没有人可以访问 Test2 中的任何东西。并且没有任何东西可以扩展 Test2 类。

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

这和OP问题中的LogHolder其实是一样的,只是Test2类都是私有方法,构造函数也是私有的,并且类没有标记为静态。

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

公共类 TestRunner {
    公共静态无效主要(字符串[]参数){
        测试测试=新测试(){
        };
        test.sayHello("简");
        System.out.println("再次");
        test.sayHello("鲍勃");
    }
}
公共接口测试{
    默认无效sayHello(字符串名称){
        记录器日志 = Test2.log;
        Test2 ref = Test2.getMine.apply(this);
        int times = ref.getTimes();
        for (int i = 0; i  {
            返回lookup.computeIfAbsent(obj, (it) -> new Test2());
        };
        私人 int 调用 = 0;
        私人测试2(){
        }
        私人无效setCalls(int调用){
            this.calls = 调用;
        }
        private int getCalls() {回电;}
        private int getTimes() {return ++calls;}
    }
}

【讨论】:

  • 你基本上改变了解决方案,OP在打开这个问题时已经有了,通过添加很多东西,OP没有要求。 Test2 类仍然具有 public 可见性,并且无论您是否明确声明它仍然是 static。添加private 构造函数是一个小的改进,但不会改变整个类在Test 接口的API 中可见的事实。没有理由将不鼓励的实例引入数据映射,因为 OP 从未要求将数据关联到实例。这真的值得挖掘一个 >1.5 年前的问题吗?
  • 我只是在演示可以做什么,不管你喜欢与否。除了神秘的 LogHolder 助手类的可见性之外,该解决方案没有任何问题。我想很多人会很高兴看到他们的选择。在默认方法中需要一个记录器是真实的,这(和 OP)提供了一种方法来做到这一点。这只是确保 LogHolder 不可扩展或实例化。
【解决方案2】:

如果您不想将 LogHolder 类公开给公众,请不要使其成为 interface 的成员类。使其成为成员类没有任何好处,您甚至不保存输入,因为无论如何您都必须使用持有者类的名称来限定字段访问,无论它是成员类还是同一个包中的类:

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

【讨论】:

  • 是的,但这并不限制同一包的其他成员访问记录器本身。也许我太纯粹了。体面的程序员几乎不会使用不同类的记录器,我猜也不会让它通过代码审查。
  • 由于interfaces 无法拥有private 成员,因此无法绕过它。但即使有办法将LogHolder 设为private 类,也不会阻止其他类自己调用getLogger(WithTimeout.class) 来获取相同的记录器实例,因此不值得付出努力。将类从 interface 中移出是有用的,以避免使用实现工件污染 API,但记录器不应该与安全相关。
  • 您可以更进一步,将 Logger 设为私有。然后,即使有人可以从包范围访问 LogHolder,他们也无法访问该记录器,因为它是私有的。但是您的界面可以完全一样地使用它。
  • @Saint Hill:这只有在LogHolder 是一个嵌套类的情况下才有效,但随后,它将隐含为public,这就是这个问题的全部内容。您只能拥有private LOGGER 字段或非public LogHolder 类。
  • @Holger 确实如此。但是该类可以公开可见,只是不允许其他任何人使用它。任何人都将永远能够看到其中包含一些奥秘。请看下面我的回答。现在告诉我,它不符合 OP 的要求:) 我不建议这样做,但作为 LogHolder 类,它非常有用。我不确定它是否有任何其他用途,而不是说对默认方法的调用进行绝密记录(邪恶的笑声
猜你喜欢
  • 1970-01-01
  • 2019-01-28
  • 1970-01-01
  • 2018-06-13
  • 2019-03-03
  • 2019-11-14
  • 2019-04-20
  • 1970-01-01
  • 2018-08-28
相关资源
最近更新 更多