【问题标题】:Are interfaces a valid substitute for utility classes in Java 8? [duplicate]接口是 Java 8 中实用程序类的有效替代品吗? [复制]
【发布时间】:2015-09-03 11:51:51
【问题描述】:

在过去十年左右的时间里,我一直在为我的 Java 实用程序类使用以下模式。该类仅包含静态方法和字段,声明为final,因此无法扩展,并且具有private 构造函数,因此无法实例化。

public final class SomeUtilityClass {
    public static final String SOME_CONSTANT = "Some constant";

    private SomeUtilityClass() {}

    public static Object someUtilityMethod(Object someParameter) {
        /* ... */

        return null;
    }
}

现在,随着 Java 8 中 static methods in interfaces 的引入,我最近发现自己在使用实用程序接口模式:

public interface SomeUtilityInterface {
    String SOME_CONSTANT = "Some constant";

    static Object someUtilityMethod(Object someParameter) {
        /* ... */

        return null;
    }
}

这让我摆脱了构造函数,以及许多隐含在接口中的关键字(publicstaticfinal)。

这种方法有什么缺点吗?使用实用程序类而不是实用程序接口有什么好处吗?

【问题讨论】:

  • @SotiriosDelimanolis:它将类的实现细节放入类的公共 API 中。我相信存在静态导入的具体原因之一是让人们不太愿意这样做。 Oracle documentation on static imports 特别指出常量接口反模式是一个非常糟糕的主意。
  • 我还没有完全相信......如果接口没有公开任何抽象方法。但这实际上并不重要,因为这些方法不是继承的。我真的不想只为这些领域而经历所有这些。
  • 作为人本人(Joshua Bloch;将常量接口创造为反模式的人):“API 应该易于使用且难以误用。它应该容易做简单的事情;可能做复杂的事情;做错事是不可能的,或者至少是困难的”。根据我对少数使用常量接口(例如 ASM)的主流 API 的经验,我一直发现实现接口(来自 ASM 的操作码)比编写静态导入更容易。我想这取决于你对设计的严格程度
  • @Vince Emigh:这取决于您使用/配置 IDE 的能力。在最好的情况下,IDE 会自动建议您使用常量并在必要时插入import static,但它绝不会建议您使用implement 和实际上并未用作真实类型的interface(没有启发式提示IDE)。
  • 请注意,您可以通过在其签名中添加一个包含对非public 类型的引用的抽象方法来防止interface 的任意实现。然后,包外的实现是不可能的。然而,这并不能提高清晰度,因为抽象方法将出现在public API 中,开发人员会开始怀疑该方法的目的,当通过尝试实现接口的可能性来发现时,他们会认为必须有一个错误。

标签: java class interface java-8 utility-method


【解决方案1】:

只有当你期望有人会实现它时,你才应该使用接口。例如,java.util.stream.Stream 接口有一堆静态方法,它们可以位于 Java 8 之前的一些StreamsStreamUtils 类中。但是它是一个有效的接口,它也具有非静态方法并且可以实现。 java.util.Comparable 是另一个例子:那里的所有静态方法都只支持接口。您不能禁止用户实现您的公共接口,但对于实用程序类,您可以禁止他们实例化它。因此,为了代码清晰,我建议不要使用接口,除非它们打算被实现。

关于@saka1029 答案的说明。虽然您确实不能在同一个接口中定义辅助私有方法和常量,但在同一个包中创建一个包私有类(如MyInterfaceHelper)不是问题,它将包含所有必要的实现相关的东西。一般来说,包私有类可以很好地向外界隐藏您的实现细节。

【讨论】:

  • 我也认为代码清晰确实是一个值得关注的问题。
  • 附带说明,Java 9 允许 interfaces 中的 private 方法,因此您的答案不依赖于该功能的缺失是一件好事,但即使那么。
  • Java 9 应该引入私有方法openjdk.java.net/jeps/213
  • @Holger haha​​ - 你比我早了 12 秒,发帖后 5 小时...我想知道发生这种情况的概率是多少 :-)
  • @assylias:我首先使用 jdk 快照进行了尝试。所以写一个小界面的时间和运行javac决定了我发评论的时间……
【解决方案2】:

基于将常量接口模式创造为反模式的人,我想说虽然您不打算让客户端实现接口,但它仍然是可能的,可能更容易,并且应该'不允许

API 应该易于使用且难以误用。做简单的事情应该很容易;可以做复杂的事情; 而且不可能,或者至少很难做错事。

虽然如下所述,但实际上取决于目标受众


许多易于使用的设计模式受到很多批评(上下文模式、单例模式、常量接口模式)。哎呀,即使是诸如得墨忒耳法则之类的设计原则也因过于冗长而受到批评。

我不想这么说,但这类决定是基于意见的。尽管上下文模式被视为一种反模式,它在 Spring 和 Android SDK 等主流框架中很明显。它归结为环境以及目标受众。

我能找到的主要缺点被列为Constant Interface wiki中“缺点”下的第三个列表:

如果在将来的版本中需要二进制代码兼容性,则常量接口必须永远保持为接口(它不能转换为类),即使它没有被用作传统意义上的接口。

如果您曾经想过“嘿,这实际上不是合同,我想强制执行更强大的设计”,您将无法更改它。但正如我所说,这取决于你;也许您将来不会在意​​更改它。

最重要的是,@TagirValeev 提到的代码清晰。接口具有被实现的意图;如果您不希望有人实现您提供的 API,请不要使其可实现。但我相信这围绕着“目标受众”的说法。不会撒谎,我支持你的基础不那么冗长,但这取决于我的代码是为谁服务的;不想为可能会被审查的代码使用常量接口。

【讨论】:

    【解决方案3】:

    你不应该使用接口。 接口不能有私有常量和静态初始化器。

    public class Utility {
    
        private Utility() {}
    
        public static final Map<String, Integer> MAP_CONSTANT;
        static {
            Map<String, Integer> map = new HashMap<>();
            map.put("zero", 0);
            map.put("one", 1);
            map.put("three", 3);
            MAP_CONSTANT = Collections.unmodifiableMap(map);
        }
    
        private static String PRIVATE_CONSTANT = "Hello, ";
    
        public static String hello(String name) {
            return PRIVATE_CONSTANT + name;
        }
    }
    

    【讨论】:

    • 首先不应该有全局可变状态(单例是反模式的原因)。我不觉得这是一个足够好的理由
    【解决方案4】:

    我认为它会起作用。我认为变量 SOME_CONSTANT 在您的 SomeUtilityInterface 中默认为 static final,即使您没有明确说明。所以,它可以作为一个实用程序工作,但你不会有一些可变性问题,而普通类不会有所有成员变量都必须是 final 的吗?只要这不是您的默认方法的特定实现的问题,我想不出问题。

    【讨论】:

    • 绝对有效。并且接口字段隐式为publicstaticfinal,所以我认为不存在任何可变性问题。
    • 通常final成员只能在构造函数中修改(不能在包含方法中)?那么,也许它们可以在实现类的构造函数中设置?你试过吗?
    • final成员可以在声明或构造函数中初始化,但只能初始化一次。由于接口中的字段默认为public static final,并且在声明时已经初始化,所以我不明白你描述的内容是如何完成的。
    • 是的,我认为这不是一个好主意。我通常使用 Spring 自动连接实用程序类和实例成员,因此将其作为常规类确实很有效。
    猜你喜欢
    • 2016-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-20
    • 2013-02-17
    • 1970-01-01
    • 2010-11-20
    相关资源
    最近更新 更多