“静态接口”是一种好习惯吗?


13

我刚刚注意到,在接口中可以选择使用静态方法。与接口的静态字段相同,有一个有趣的行为:这些不是继承的。

我不确定它在要实现的实际接口中是否有用。但是,它使程序员能够创建仅仅是静态内容的信封的接口,例如实用程序类。

一个简单的例子只是全局常量的包络。与班级相比,您可以轻松地注意到public static final那些假定的样板丢失了(使它不太冗长)。

public interface Constants {
    String LOG_MESSAGE_FAILURE = "I took an arrow to the knee.";
    int DEFAULT_TIMEOUT_MS = 30;
}

您还可以使事情变得更复杂,例如配置键的伪枚举。

public interface ConfigKeys {
    static createValues(ConfigKey<?>... values) {
        return Collections.unmodifiableSet(new HashSet(Arrays.asList(values)));
    }

    static ConfigKey<T> key(Class<T> clazz) {
        return new ConfigKey<>(clazz);
    }

    class ConfigKey<T> {
        private final Class<T> type;
        private ConfigKey(Class<T> type) {
            this.type = type;
        }
        private Class<T> getType() {
            return type;
        }
    }
}

import static ConfigKeys.*;
public interface MyAppConfigKeys {
    ConfigKey<Boolean> TEST_MODE = key(Boolean.class);
    ConfigKey<String> COMPANY_NAME = key(String.class);

    Set<ConfigKey<?>> VALUES = createValues(TEST_MODE, COMPANY_VALUE);

    static values() {
        return VALUES;
    }
}

您也可以通过这种方式创建一些实用程序“类”。但是,在实用程序中,使用私有或受保护的助手方法通常很有用,而在类中却不太可能。

我认为这是一个不错的新功能,尤其是静态成员没有被继承的事实是一个有趣的概念,仅引入接口。

我想知道您是否可以认为这是一个好习惯。虽然代码风格和最佳实践不是不言而喻的,并且仍有意见的余地,但我认为通常有充分的理由支持意见。

我对使用这两种模式的原因(而不是原因)更感兴趣。


请注意,我不打算实现这些接口。它们仅仅是其静态内容的信封。我只打算使用常量或方法,并且可能使用静态导入。


1
我对此的下意识反应是,它只会导致坏事。即使公共静态方法本质上只是命名空间的全局函数。感觉就像允许接口中的任何实现代码都违背其缺少契约定义的实现的目的。我说将部分实现留给Abstract类。我将需要阅读为什么要添加它。
阿德里安

我现在必须走,但是明天我会写一些东西。简而言之,就是在接口具有明确的目的之前,该目的与抽象类的目的明显分开。现在,它们以违反接口的语言不可知定义的方式大量重叠。另外,在某些情况下,如果它们实现默认方法,则无法再从多个接口继承。因此,现在有多个接口继承的例外,以前没有任何例外。由于Java大大偏离了接口的通用定义...
Adrian

这也可能使新开发人员感到困惑,这些新开发人员试图正确处理基本的OOP概念,例如接口,AbstractClases,何时使用合成而不是继承等等。
阿德里安

Aliester,您正在谈论default方法。我在说static方法和领域。这些不是继承的,所以它们不会破坏多重继承。
Vlasec

Answers:


1

一个简单的例子只是全局常量的包络。

将常量放在接口上之所以称为,是Constant Interface Antipattern因为常量通常只是实现细节。常量界面上的Wikipedia文章总结了其他缺点。

您也可以通过这种方式创建一些实用程序“类”。

确实,Java文档指出,静态方法可以使组织帮助程序方法更加容易,例如,从默认方法调用帮助程序方法时。


1
我不认为这实际上是对所提问题的答案,因为您可以在没有此新功能的情况下完成这两项工作。
阿德里安

嗯,因此,根据文档,它旨在用作帮助方法。但是,它也是公开的,这与之矛盾。因此,也许它也旨在帮助实现接口的类。但是它们不是继承的,因此您必须使用静态导入才能使它看起来像继承了它。好吧,感谢您在文档中查找它。
Vlasec

2
如果常量在某种程度上是接口描述的API的一部分,则将常量放入接口绝对没有错。唯一的反模式是将常量放入没有方法的接口中,并让类实现该接口只是为了引用没有类前缀的常量。这是对接口的滥用,并且由于引入了静态导入,因此已经完全过时了。
Michael Borgwardt

1

如问题中所述,该接口并不旨在实现(或实例化)。因此,我们可以将其与具有私有构造函数的类进行比较:

  • 不能通过no super()调用来扩展该类,但是可以“实现”接口
  • 没有反射就无法实例化该类,而可以像这样简单地将接口轻松实例化为一个匿名类: new MyInterface() {};

这种比较有利于类,因为它允许更多的控制。就是说,类也不打算成为静态对象的持有者,您希望它具有实例。好吧,没有完美的选择。

关于从“静态接口”继承的问题也很奇怪:

  • “实现”类继承字段,但不继承静态方法
  • 扩展接口根本不继承任何静态成员
  • (虽然扩展类的类继承了每个非私有成员……并且由于不能通过接口进行扩展,所以混淆的空间较小)

这些可能是将其视为错误模式并在这些情况下继续使用静态类的原因。但是,对于沉迷于语法糖的人来说,即使有不利因素,也可能仍然很诱人。


...无论如何,团队的编码约定可以解决这些问题。对我来说,尽管它不像拥有私有构造函数的类那样紧紧地束缚着程序员的手,但它所承受的风险仍然小于设置者,并且经常使用设置者。
Vlasec

0

与@MarkusPscheidt一样,这个想法本质上是Constant Interface反模式的扩展。最初,这种方法被广泛使用,以允许许多不同的类继承一组常量。之所以将其视为反模式,是因为在实际项目中使用此方法遇到的问题。我没有理由认为这些问题仅与常数有关。

可能更重要的是,实际上没有必要这样做。请改用静态导入,并明确说明要导入的内容以及从何处导入。


是的,静态导入听起来比常量继承更好,它可以与类和接口的静态成员一起使用。它仅在类/接口内部可见。
Vlasec

@Vlasec对,但是不需要在接口上执行此操作。使用类和接口执行此操作之间的唯一区别是,您可以从多个接口继承。此类实用程序类的标准做法是使构造函数私有化,以防止实例化或扩展。在类上的接口中执行此操作没有任何好处。它只是为滥用提供了方便。
JimmyJames

我可以在接口的常量中看到值。接口倾向于定义方法,并且方法有时接受常量作为参数。例如:interface ColorFilter {Image applyGradient(int colorSpace, int width, byte[] pixels); }。我可以想像有一些是不同的常数RGBRGBACMYKGREYSCALEINDEXED
斯泰恩德威特

@StijndeWitt这不是最坏的事情,但是您可以为此目的在接口上使用枚举或嵌套类。真正的问题是使用它通过继承将静态变量带入许多类的范围。这种方法显然不如使用静态导入。
JimmyJames

0

我想说的是,如果您对如何正确使用新功能感兴趣,请查看JDK中的代码。接口上的默认方法使用非常广泛,并且很容易找到,但是接口上的静态方法似乎很少见。一些例子包括java.time.chrono.Chronologyjava.util.Comparatorjava.util.Map,和不少在接口java.util.functionjava.util.stream

我看过的大多数示例都涉及到这些API的使用,这些API可能很常见:例如,时间API中不同类型之间的转换,例如,可能经常在传递给函数的对象或包含在其中的对象之间进行比较。集合。我的要点:如果您的API的一部分需要灵活,但是您可以使用一些默认值覆盖80%的用例,请考虑在接口上使用静态方法。鉴于此功能似乎很少在JDK中使用,因此在我自己的代码中使用它时,我会非常保守。

您的示例看起来并不像我在JDK中看到的那样。对于这些特定情况,几乎应该确定要创建一个enum实现所需接口的。接口描述了一组行为,实际上没有业务维护其实现的集合。


-1

我想知道是否有理由不使用它。

嗯,与旧版JVM的兼容性如何?

您一般认为这是个好主意吗?

否。这是一种向后不兼容的更改,使语言的表现力更加明显。两个大拇指朝下。

在某些模型情况下,强烈建议您上课吗?

我强烈建议使用类来包含实现细节。一个接口实际上只是说“此对象支持XYZ操作”,它与您可能考虑放入接口声明中的任何静态方法无关。此功能就像带有内置迷你冰箱的躺椅。当然,它有一些利基用途,但它的重量不足以成为主流。

更新:请不要再投票。显然,有些人认为接口中的静态方法是蜜蜂的膝盖,在此我投降了。我将尽快删除此答案,但所附的评论是一个有趣的讨论。


评论不作进一步讨论;此对话已转移至聊天
maple_shaft

如果我否决了您的问题,那么我会这样做,因为我们的前两个答案不是真正的答案。新版本的语言带来了使工具变得更容易的新工具,这正是这些新版本的目的。第三个答案有点丢在前两个破烂的废墟中。
Vlasec
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.