【问题标题】:force subclasses to include constant in abstract java class强制子类在抽象 java 类中包含常量
【发布时间】:2012-08-10 07:07:24
【问题描述】:

我有一个抽象类,我希望所有子类都根据实现定义一个常量,它主要是关于类实现的元数据。

在超类中:

protected static final String OBJECT_NAME;
protected static final String OBJECT_DEF;

然后在子类中:

protected static final String OBJECT_NAME = "awesome class";
protected static final String OBJECT_DEF = "an awesome class that is also great";

有没有办法强制类的实现声明一个常量?

【问题讨论】:

  • 可能值得注意的是,具有可能不同值的常数不是常数!所以跟着彼得斯回答吧。

标签: java inheritance constants


【解决方案1】:

您可以强制子类定义一个方法,该方法可以是该类的常量。

protected abstract String objectName();
protected abstract String objectDef();

注意:如果您有子类的子类,这些子类可能会“忘记”覆盖这些方法。

【讨论】:

  • 你能得到的最好的。由于 O.P. 不能完全按照他的意愿去做,这当然是最好的方法。
  • 谢谢!没想到这一点(显然)。有没有办法保证子子实现的静态类实现了这些方法呢?
【解决方案2】:

这样就行不通了。我会将这些定义为抽象函数:

protected abstract String objectName();
protected abstract String objectDef();

在子类中:

private static final String OBJECT_NAME = "awesome class";
private static final String OBJECT_DEF = "bla";

protected String objectName(){
    return OBJECT_NAME;
}

protected String objectDef(){
    return OBJECT_DEF;
}

方法不是static,但我认为这是最接近你想要的方法。

【讨论】:

    【解决方案3】:

    这是另一种方法。我喜欢它,因为它很严格,允许我们将大部分代码(包括 getter)放入父类中,使其子类保持整洁。

    abstract class Parent {
        private final int age;
        private final String name;
    
        Parent(int age, String name) {
            this.age = age;
            this.name = name;
        }
    
        int getAge() {
            return age;
        }
    }
    
    class Child extends Parent {
    
        Child() {
            super(18, "John");
            // any other Child specific constructor logic
        }
    }
    
    class Main {
        public static void main(String[] args) {
            var child = new Child();
            System.out.println(child.getAge()); // Will print 18
        }
    }
    

    注意:Child 不继承 Parent 的构造函数,因此在初始化 Child 时不存在意外使用 wearg 构造函数的危险。

    【讨论】:

      【解决方案4】:

      最好选择一个接口,因为您没有合理的超类默认值:

      interface HasObjectNameAndDef {
          public String getObjectName();
          public String getObjectDef(); 
      }
      

      然后让你的实际类实现接口。作为奖励,您将获得更灵活的测试。

      【讨论】:

        【解决方案5】:

        没有。你不能。

        你甚至不能在你的抽象超类中声明常量而不给它们分配具体的值。在其他情况下,您将收到错误消息。您应该在声明期间或在类构造函数中初始化 final 字段。

        您可以声明将初始化变量的抽象方法(但这会更加模糊)。像这样:

        protected String OBJECT_NAME = getObjectNameValue();
        
        public abstract String getObjectNameValue();
        

        但在这种情况下,您的变量和方法不应该是静态的。因为您不能将静态方法定义为抽象 (look here for explanation)。

        或者你可以像 Peter Lawrey 建议的那样直接使用方法而不是变量,这样会更具可读性。

        【讨论】:

          【解决方案6】:

          我知道你在说什么。

          例如,我希望有一个接口(我称之为 ExecutableList)有一个名为 USER_FRIENDLY_NAME 的持续字符串字段

          public interface ExecutableList<T extends ExecutableList<T,E>,E> //This is type parameter
              extends List<E>,                                             //self referencing. Trust
                      Comparable<E>,                                       //me, it has its uses.
          {
            /** This would declare a constant but leave it to sub-interfaces/classes to define it. */
            abstract String USER_FRIENDLY_NAME;
          
            //Define the rest of your interface
          
          }
          

          不幸的是,这在 Java 中是不可能的。当您在 Java 接口中声明一个字段时,它暗示了修饰符 publicstaticfinal。未来对 Java 编程语言的更改可能允许添加 abstract,从而使隐含的修饰符 public abstract static final String USER_FRIENDLY_NAME;,但是虽然您可以为此明确定义规则,但 abstract 一词的使用会令人困惑,因为 @ 987654329@ 和final 通常被理解为定义相反的含义。

          我希望未来的 Java 版本会实现这一点。我听说 Java 9 中将添加私有接口方法(用于在默认方法之间使用),但还没有关于该语言添加的消息。我敢肯定,首先要考虑许多向后兼容性问题。如果 Oracle 的任何人读到此内容,请让我们在接口中声明常量并让继承类/接口定义它们!!!

          与此同时,您唯一的选择是在您的接口或抽象类中定义一个String getUserFriendlyName(); 方法。没有那么优雅,也可能没有那么高效,但您目前别无选择。

          【讨论】:

            猜你喜欢
            • 2012-05-09
            • 2010-09-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-09
            • 2014-09-11
            • 1970-01-01
            • 2012-10-08
            相关资源
            最近更新 更多