【问题标题】:Should a class implement a constants-only interface?一个类应该实现一个仅常量的接口吗?
【发布时间】:2010-01-14 10:38:31
【问题描述】:

今天查看ZipEntry类,发现如下:

public class ZipEntry implements ZipConstants, Cloneable

ZipConstants 没有定义任何方法 - 只有常量 (static final int LOCHDR = 30)

然后我突然想到,使用常量实现接口可以让您直接访问这些常量,就好像它们是在类本身中定义的一样。例如:

public interface Constants {
    static final int CONST = 2;
}

public class implements Constants {
    int doSomething(int input) {
        return CONST * input;
    }
}

除了:

  • 起初令人困惑的是常量的来源
  • 使用接口定义常量被认为是错误的

我很好奇,因为这绝对不是很常见的做法。

【问题讨论】:

    标签: java


    【解决方案1】:

    不使用它的另一个原因:

    从 Java 5 开始,有一个“干净”的语言特性可以实现相同的目标:static imports

    实现接口以使用常量基本上是 Java-5 之前的 hack,用于模拟静态导入。

    【讨论】:

    • (+1),它没有完全模拟它,因为它不适用于方法的静态导入
    • 其实这个论点也有一些循环逻辑。我们不会只继承常量类,因为您可以使用静态导入。但是既然我们可以继承接口,为什么还要使用静态导入呢?甚至称其为“黑客”而不说明为什么将其视为“黑客”,这就像在重复“坏风格”的口头禅。
    • 不同之处在于静态导入是专门为实现这一点而引入的语言特性,而接口的用途则大不相同;因此,使用这样的界面是一种 hack;我认为这是不言而喻的。
    • 坦率地说。静态导入不过是编译器功能。只需查看静态导入的反编译版本即可。您可以看到对任何变量的引用都与接口一起。
    • @Ravisha:整个 Java 语言(以及所有其他高级语言)“只不过是一种编译器功能”。
    【解决方案2】:

    这并不像你想象的那么罕见,例如在Parasofts JTest的静态分析中,常量应该在class中声明的规则和常量应该在interfaces中声明的规则都是存在,并由项目在它们之间进行选择。

    也就是说,在我的所有项目中,我都不允许在接口中定义常量。与开发人员必须检查一个类中使用的常量实际上是否与另一个类中的常量相同(或不同)的情况相比,创建一个有意义的类并明确常量的上下文使代码更具可读性和可维护性。 )

    【讨论】:

      【解决方案3】:

      我认为使用共享常量的接口是混淆两个不同概念的一个例子:

      1. 代码重用
      2. 子类型化

      根据我的经验,使用子类或接口实现只是为了防止代码重复会导致问题。您的代码变得更加脆弱。例如,有人可能会意外地重新定义常量 - 特别是当您的类层次结构有多个类时。

      通常最好使用组合来保持代码干燥。

      以这种方式使用继承的另一个问题是,这种类型的继承通常构成类 API 的一部分。类的层次结构在类外部可见。这打破了封装。您无需在类之外公开对常量的使用,它们与您选择实现类的方式有关,而不是其 API 的一部分(在您的示例中)。

      这会导致可怕的向后兼容性问题。其他人可能会出现并编写如下代码:

      public interface Constants {
         static final int CONST = 2;
      }
      
      public class MyClass implements Constants {
         int doSomething(int input) {
          return CONST * input;
         }
      }
      
      public class ThirdPartyClass {
         int doSomethingElse(int input) {
           return MyClass.CONST + input;
         }
      }
      

      现在,如果您决定不再需要在 MyClass 中使用 CONST,那么您将陷入困境。因为 ThirdPartyClass 已经创建了对 MyClass 中可用的 CONST 的依赖。

      你可以这样结束。 MyClass 没有使用接口中的任何常量,但仍然必须实现它。

      public interface Constants {
         static final int CONST = 2;
      }
      
      public class MyClass implements Constants {
         int doSomething(int input) {
          return input;
         }
      }
      
      public class ThirdPartyClass {
         int doSomethingElse(int input) {
           return MyClass.CONST + input;
         }
      }
      

      简而言之;永远不要这样做!

      【讨论】:

      • 我不打算这样做,我只是好奇为什么要在 sun-provided API 中完成它
      • 可能是缺乏经验?在我意识到它可能导致的问题之前,我过去已经这样做了。或者,也许很久以前它只是被决定为一个代码约定,而现在 sun 的开发人员觉得他们被困在了它的后面?我猜……
      【解决方案4】:

      ...因为使用接口定义常量被认为是错误的

      这是不做某事的糟糕理由。其实这根本不是理由。

      编辑

      考虑一下。

      • XXX 风格不好的原因是 YYY。
      • 你应该做 XXX 的原因是它的风格不好。

      不做XXX有多少实质性的理由?一个或两个?

      如果答案是 2,我可以通过添加额外的细细原因链使其成为 3、4、5 等等。例如“你不应该做 XXX 的原因是因为它是一个坏主意。” “这是一个坏主意的原因是它的风格不好”。等等。这显然是愚蠢的。

      不,不做XXX的真正原因是YYY,“Bad style”的原因不是实质性的原因。而是说因为 YYY 和 ZZZ 以及任何其他实质性原因而不要做 XXX 的捷径。

      事实上,即使是 OP 的“令人困惑”的原因也没有完全说明。为什么会让人困惑?

      因为接口通常是一种类型,实现该接口的类是子类型。但是一个只有常量的接口在任何有用的意义上都不是一种类型,实现该接口的类在任何有用的意义上都不是子类型。归根结底,这就是实现纯常量接口被称为不良风格和“反模式”的真正原因,也是 Java 5 中添加静态导入的主要原因。

      【讨论】:

      • 因为无论如何都是代码风格的问题,有另一个代码风格的问题有一个代码风格的原因并不奇怪。
      【解决方案5】:

      不,这也称为“常量接口反模式”。另一种方法是编写一个定义常量的具体类,然后使用静态导入。

      类常量

      package util;
      
      public class Constants {
          public static final String CONSTANT_STRING = "abc";
      
          private Constants() {
              throw new AssertionError(); 
          }
      }
      

      课堂测试

      import static util.Constants.CONSTANT_STRING;
      
      public class Test {
          System.out.println(CONSTANT_STRING);
      }
      

      Wikipedia

      了解更多详情。

      【讨论】:

      • 我知道什么是静态导入 ;)
      【解决方案6】:

      不将常量放入接口的原因之一是,如果您将接口公开给第三方,他们就可以访问您的常量。 开始这似乎不是一个坏主意,但想象一下,如果您想更改常量的值,但人们仍在使用旧界面。

      当您向界面添加某些内容时,它可能会一成不变,因此请仅添加您希望其他人看到和使用的内容。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-17
        • 2010-10-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-02-11
        相关资源
        最近更新 更多