Java 为什么不是抽象字段?

Java 为什么不是抽象字段?,java,methods,field,abstract,Java,Methods,Field,Abstract,为什么Java类不能像拥有抽象方法一样拥有抽象字段 例如:我有两个类扩展了相同的抽象基类。这两个类各自有一个相同的方法,但其中有一个字符串常量,它恰好是一条错误消息。如果字段可以是抽象的,我可以将这个常量抽象并将该方法拉入基类。相反,我必须创建一个抽象方法,在本例中称为getErrMsg() 为什么我不能从一开始就把这个字段抽象化呢?Java是否可以设计为允许这样做?显然,它可以设计为允许这样做,但在封面下,它仍然必须进行动态调度,并因此进行方法调用。Java的设计(至少在早期)在某种程度上是一

为什么Java类不能像拥有抽象方法一样拥有抽象字段

例如:我有两个类扩展了相同的抽象基类。这两个类各自有一个相同的方法,但其中有一个字符串常量,它恰好是一条错误消息。如果字段可以是抽象的,我可以将这个常量抽象并将该方法拉入基类。相反,我必须创建一个抽象方法,在本例中称为
getErrMsg()


为什么我不能从一开始就把这个字段抽象化呢?Java是否可以设计为允许这样做?

显然,它可以设计为允许这样做,但在封面下,它仍然必须进行动态调度,并因此进行方法调用。Java的设计(至少在早期)在某种程度上是一种极简主义的尝试。也就是说,如果语言中已有的其他功能可以很容易地模拟新功能,那么设计人员试图避免添加新功能。

我认为这没有任何意义。您可以将函数移动到抽象类,只需重写某些受保护的字段。我不知道这是否适用于常数,但效果是一样的:

public abstract class Abstract {
    protected String errorMsg = "";

    public String getErrMsg() {
        return this.errorMsg;
    }
}

public class Foo extends Abstract {
    public Foo() {
       this.errorMsg = "Foo";
    }

}

public class Bar extends Abstract {
    public Bar() {
       this.errorMsg = "Bar";
    }
}

因此,您的观点是希望在子类中强制执行
errorMsg
的实现/重写/任何内容?我以为您只是想将方法放在基类中,而不知道如何处理该字段。

阅读您的标题,我以为您指的是抽象实例成员;我看不出它们有什么用处。但抽象静态成员完全是另一回事

我经常希望我可以在Java中声明如下方法:

public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

基本上,我希望坚持父类的具体实现提供一个带有特定签名的静态工厂方法。这将允许我使用
class.forName()
获取对具体类的引用,并确保我可以按照我选择的约定构造一个。

您可以通过在抽象类中设置一个在其构造函数中初始化的最终字段(未测试的代码)来完成所描述的操作:


如果您的子类“忘记”通过超级构造函数初始化final,编译器将发出错误警告,就像未实现抽象方法一样。

另一个选项是在基类中将字段定义为公共(final,如果您愿意),然后在基类的构造函数中初始化该字段,取决于当前正在使用的子类。这有点可疑,因为它引入了循环依赖。但是,至少它不是一个可以改变的依赖关系——也就是说,子类要么存在要么不存在,但是子类的方法或字段不能影响
字段的值

public abstract class Base {
  public final int field;
  public Base() {
    if (this instanceof SubClassOne) {
      field = 1;
    } else if (this instanceof SubClassTwo) {
      field = 2;
    } else {
      // assertion, thrown exception, set to -1, whatever you want to do 
      // to trigger an error
      field = -1;
    }
  }
}

嗯,我当然可以这么做。我已经想到了。但是为什么我必须在基类中设置一个值,而我知道我将在每个派生类中重写该值?你的论点也可以用来论证允许抽象方法也没有任何价值。这样,不是每个子类都必须重写这个价值。我还可以定义
protectedstringerrormsg以某种方式强制我在子类中设置值。它类似于那些实际实现所有方法作为占位符的抽象类,因此开发人员不必实现每个方法,尽管他们不需要
不强制您在子类中设置值。如果不将值设置为null(如果您不将其设置为“强制”),那么您将如何称呼非强制?@felix,强制和契约之间有区别。这篇文章以抽象类为中心,抽象类反过来表示任何子类都必须履行的契约。所以,当我们谈论有一个抽象字段时,我们是说任何子类都必须实现该字段的值,每个契约。您的方法不能保证任何价值,也不能像合同那样明确预期的内容。非常非常不同。没有一个编辑器在这里尝试它,但这也是我要发布的内容….“编译器将给出警告”。实际上,子构造函数将尝试使用不存在的noargs构造函数,这是一个编译错误(不是警告)。在@Stephen C的注释中添加“like when a abstract method not implemented”也是一个错误,而不是警告。回答得好-这对我来说很有效。我知道它发布已经有一段时间了,但是我认为
Base
需要声明
abstract
。我仍然不喜欢这种方法(尽管这是唯一的方法),因为它会给构造函数添加一个额外的参数。在网络编程中,我喜欢创建消息类型,其中每个新消息实现一个抽象类型,但必须有一个唯一的指定字符,如AcceptMessage的“a”和LoginMessage的“L”。这些确保了自定义消息协议能够将此区别字符放到线路上。这些都是类的隐式,所以它们最好作为字段使用,而不是传递给构造函数。。。这可能意味着您使用的反射太多了!!对我来说,这听起来是一个奇怪的编程习惯。forName(…)闻起来像是一个设计缺陷。现在我们基本上总是使用SpringDI来完成这类事情;但在这个框架被了解和流行之前,我们会在属性或XML文件中指定实现类,然后使用Class.forName()加载它们。我可以给你一个它们的用例。缺少“抽象字段”几乎不可能在抽象泛型类中声明泛型类型的字段:
@autowiredt fieldName
public abstract class Base {
  public final int field;
  public Base() {
    if (this instanceof SubClassOne) {
      field = 1;
    } else if (this instanceof SubClassTwo) {
      field = 2;
    } else {
      // assertion, thrown exception, set to -1, whatever you want to do 
      // to trigger an error
      field = -1;
    }
  }
}