为什么不是抽象领域?

为什么 Java 类不能像使用抽象方法那样使用抽象字段呢?

例如: 我有两个扩展同一个抽象基类的类。这两个类都有一个相同的方法,只有一个 String 常量(恰好是一条错误消息)除外。如果字段可以是抽象的,那么我就可以将这个常量变为抽象的,并将该方法提取到基类中。相反,我必须创建一个抽象方法,在本例中称为 getErrMsg(),它返回 String,在两个派生类中覆盖该方法,然后我可以提取该方法(现在调用抽象方法)。

为什么我不能一开始就把字段抽象化呢? Java 的设计能允许这样吗?

80638 次浏览

Obviously it 可以 have been designed to allow this, but under the covers it'd still have to do dynamic dispatch, and hence a method call. Java's design (at least in the early days) was, to some extent, an attempt to be minimalist. That is, the designers tried to avoid adding new features if they could be easily simulated by other features already in the language.

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

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 字段来完成你所描述的工作,这个字段在它的构造函数中被初始化(未经测试的代码) :

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,编译器将给 一个警告一个错误,就像没有实现抽象方法一样。

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