可以使用 abtract 方法初始化抽象基类中的最终成员吗?

Is it ok to initialize final member in abstract base class using abtract method?

提问人:Y.F. 提问时间:9/20/2023 更新时间:9/27/2023 访问量:79

问:

我想知道我是否可以使用由 abtract 方法实现的 abtract 方法的结果初始化抽象基类中的最后一个字段。 我知道派生的 ctor 会首先调用基 ctor,所以我保证 abtract 方法的实现也只依赖于派生类的声明中定义的最终字段。 我尝试以下代码,它似乎有效。

abstract class Base {
    protected final String name = calName();
    protected abstract String calName();
}

public final class Foo extends Base {
    private final String id = "42";
    @Override
    protected String calName() {
        return String.format("WithId%s", id);
    }
    public void display() {
        System.out.printf("name: %s\n", name);
        System.out.printf("id:   %s\n", id);
    }
    public static void main(String[] args) {
        var foo = new Foo();
        foo.display();
    }
}

那么这是 java 中的合法操作,还是一个非常糟糕的主意?

Java 构造函数 初始化 基类

评论

0赞 Michael 9/20/2023
尝试在非恒定的地方 - 将其传递给 的构造函数。idFoo
4赞 Joachim Sauer 9/20/2023
最终字段很早就初始化了,此时可重写的方法意味着您基本上是在未完全初始化的对象上调用方法,因此存在很大的风险。
0赞 VGR 9/21/2023
一个更好的主意是完全删除。Base 中任何需要该名称的代码都可以...根据需要致电。更简单、更干净。protected final String namecalName()

答:

0赞 Nolequen 9/20/2023 #1

不,这是不行的。

在对象构造过程中调用抽象方法(以及任何类型的可重写方法)是一个坏主意。

此类调用可能会导致细微的错误,因为对象初始化可能会在方法调用之前发生。

0赞 Y.F. 9/27/2023 #2

感谢您的所有意见。现在我对如何处理这种情况有了一定的了解。

  1. 如果可以使用默认 ctor 直接构造该字段并在以后进行配置,则我可以在基类中将其构造为最终字段,并在派生构造阶段对其进行配置:

     abstract class Base {
         protected final Widget core = new Widget();
         public void methodThatUsesCore() { core.display(); }
     }
     public final class Foo42 extends Base {
         {
             core.configure(42, "jklmn");
         }
     }
    
  2. 如果必须使用派生类提供的信息来构造该字段,我应该将其放入派生类中,并在基类中添加一个抽象 getter:

     abstract class Base {
         protected abstract Widget getCore();
         public void methodThatUsesCore() { getCore().display(); }
     }
     public final class Foo42 extends Base {
         private final Widget core = new Widget(42, "jklmn");
         @Override
         protected Widget getCore() { return core; }
     }
    

这是一个合理的设计吗?