关于java:为什么不抽象字段?

Why not abstract fields?

为什么Java类不能有抽象的字段,比如它们可以有抽象的方法?

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

为什么我不能从抽象的领域开始呢?Java能被设计成允许这样吗?


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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,编译器将给出一个警告->strike>一个错误,就像没有实现抽象方法一样。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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的设计(至少在早期)在某种程度上是一种极简主义的尝试。也就是说,如果可以很容易地被语言中已有的其他特性模拟,那么设计者试图避免添加新特性。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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;
    }
  }
}

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

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

1
2
3
4
5
6
7
public abstract class MyClass {

    public static abstract MyClass createInstance();

    // more stuff...

}

基本上,我希望坚持父类的具体实现提供带有特定签名的静态工厂方法。这将使我能够使用Class.forName()获得一个具体类的引用,并确保我可以在自己选择的约定中构造一个具体类。