114

为什么 Java 类不能像抽象方法那样具有抽象字段?

例如:我有两个扩展同一个抽象基类的类。这两个类都有一个相同的方法,除了一个字符串常量,它恰好是一个错误消息,在它们里面。如果字段可以是抽象的,我可以使这个常量抽象并将方法拉到基类中。相反,我必须创建一个抽象方法,getErrMsg()在本例中调用,它返回字符串,在两个派生类中重写此方法,然后我可以调用该方法(现在调用抽象方法)。

为什么我不能一开始就让这个领域抽象呢?Java是否可以被设计为允许这样做?

4

5 回答 5

114

您可以通过在抽象类中有一个在其构造函数中初始化的 final 字段(未经测试的代码)来执行您所描述的操作:

abstract class Base {

    final String errMsg;

    Base(String msg) {
        errMsg = msg;
    }

    abstract String doSomething();
}

class Sub extends Base {

    Sub() {
        super("Sub message");
    }

    String doSomething() {

        return errMsg + " from something";
    }
}

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

于 2010-02-05T22:59:31.477 回答
9

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

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在子类中强制执行/覆盖/什么?我以为您只是想在基类中拥有该方法,而当时不知道如何处理该字段。

于 2010-02-05T22:56:24.243 回答
4

显然,它本来可以被设计成允许这样做的,但在幕后它仍然必须进行动态调度,因此需要进行方法调用。Java 的设计(至少在早期)在某种程度上是一种极简主义的尝试。也就是说,如果新功能可以很容易地被语言中已有的其他功能模拟,设计者会尽量避免添加新功能。

于 2010-02-05T22:53:21.637 回答
0

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

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

public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

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

于 2010-02-05T22:58:53.683 回答
0

另一种选择是将字段定义为基类中的公共字段(如果您愿意,可以选择最终字段),然后根据当前使用的子类在基类的构造函数中初始化该字段。这有点可疑,因为它引入了循环依赖。但是,至少它不是一个可以改变的依赖——即,子类将存在或不存在,但子类的方法或字段不能影响field.

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;
    }
  }
}
于 2017-06-16T15:33:01.263 回答