考虑一下Java构造函数

考虑一下Java构造函数,java,Java,在本例中: class A { public A() { // pre-init1 // post-init1 } } class B extends A { public B() { super(); // init2 } } 我想让init2先于init1,因为super()必须在一开始就出现,所以唯一的方法是添加另一个init方法: class A { public A() {

在本例中:

class A {
    public A() {
        // pre-init1
        // post-init1
    }
}

class B extends A {
    public B() {
        super();
        // init2
    }
}
我想让init2先于init1,因为super()必须在一开始就出现,所以唯一的方法是添加另一个init方法:

class A {
    public A() {
        init();
    }

    protected void init() {
        // pre-init1
        // post-init1
    }
}

class B extends A {
    public B() {
        super();
    }

    protected void init() {
        // init2
        super.init();
    }
}
我可以去掉init()方法吗

或者,我必须将final字段设置为非final。 或者,有没有办法让一个函数在init2之后执行post-init1,但不引入init()方法

编辑

这里是来自实践的代码,我想我需要这个特殊的init()来处理特殊情况

这是Spring JUnit测试的基本支持类,由于某些原因,我无法使用Spring测试中的SpringJUnit4Runner,因此我创建了自己的

// wire the bean on demand.
public static <T> T selfWire(T bean) {
    if (bean == null)
        throw new NullPointerException("bean");

    ApplicationContext context = buildAnnotationDescribedContext(bean.getClass());
    AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
    beanFactory.autowireBean(bean);

    if (bean instanceof ApplicationContextAware) {
        ((ApplicationContextAware) bean).setApplicationContext(context);
    }
    if (bean instanceof InitializingBean) {
        try {
            ((InitializingBean) bean).afterPropertiesSet();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize bean", e);
        }
    }

    return bean;
}

@Import(TestContext.class)
public abstract class WiredTestCase 
        extends Assert
        implements InitializingBean {

    // ...

    public WiredTestCase() {
        init();
        ApplicationContextBuilder.selfWire(this);
        logger.debug("WiredTestCase wired");
    }

    protected void init() {
    }

    @Overrdie
    public void afterPropertiesSet() {
    }

}

@Import({ TestDaoConfig.class })
public class WiredDaoTestCase
        extends WiredTestCase {

    public WiredDaoTestCase() {
        // init... moved to init()
    }

    protected void init() {
        // Collect entity classes from @Using annotation
        // and config the session factory.
    }

}

@Using(IcsfIdentityUnit.class)
@ImportSamples(R_ACLSamples.class)
public class R_AuthorityTest
        extends WiredDaoTestCase {

    @Inject
    R_Authority authority;

    @Inject
    ScannedResourceRegistry registry;

    @Overrdie
    public void afterPropertiesSet() {
        // Do a lot of reflection discoveries.
        // ...
        super.afterPropertiesSet();
    }

    @Test
    public void testXxx() { ... }

    // ...
}
//按需连接bean。
公共静态T自连线(T bean){
if(bean==null)
抛出新的NullPointerException(“bean”);
ApplicationContext context=buildAnnotationDescribedContext(bean.getClass());
AutowireCapableBeanFactory beanFactory=context.getAutowireCapableBeanFactory();
自动蚕豆(bean);
if(ApplicationContextAware的bean实例){
((ApplicationContextAware)bean);
}
if(初始化bean的bean实例){
试一试{
((初始化bean)bean);
}捕获(运行时异常e){
投掷e;
}捕获(例外e){
抛出新的RuntimeException(“初始化bean失败”,e);
}
}
返回豆;
}
@导入(TestContext.class)
公共抽象类WiredTestCase
扩展断言
实现初始化bean{
// ...
公共有线测试用例(){
init();
ApplicationContextBuilder.selfWire(此);
logger.debug(“WiredTestCase wired”);
}
受保护的void init(){
}
@过度
公共无效afterPropertiesSet(){
}
}
@导入({TestDaoConfig.class})
公共类WiredDataTestCase
扩展WiredTestCase{
公共WiredDataTestCase(){
//init…移动到init()
}
受保护的void init(){
//使用注释从@收集实体类
//并配置会话工厂。
}
}
@使用(icsFidentyUnit.class)
@导入示例(R\u ACLSamples.class)
公共类R_权威测试
扩展WiredDataTestCase{
@注入
R_当局;
@注入
资源登记处;
@过度
公共无效afterPropertiesSet(){
//做大量的反射发现。
// ...
super.afterPropertiesSet();
}
@试验
public void testXxx(){…}
// ...
}

代码很长,但思想很简单,在
R\u AuthorityTest
中有要注入的DAO bean,它依赖于
SessionFactory
,并且会话工厂在
wiredDataotestCase
中配置,后者是
R\u AuthorityTest
的基类。尽管有最后的字段,但我必须在WiredTestCase()之前初始化会话工厂。我不能仅在静态构造函数中初始化它们,因为我是根据
this.getClass()
上的注释动态构建持久化单元的。所以,我个人认为,有时在超级构造函数之前进行一些预初始化是合理的,也许init方法是这种情况下的唯一方法?

即使在第二个示例中,您也需要调用
B.init()
调用
super.init()
,否则
init1
的逻辑将无法执行

我尽量不使用这种
init
方法-通常在构造函数中调用虚拟方法是一个非常糟糕的主意。您还没有真正解释为什么需要在
init1
之前发生
init2
。。。我怀疑有更好的设计可用,但很难提出前进的方向,因为我们不知道您正在尝试做什么。例如,给你的超类构造函数一些参数可能是前进的方向——但我们现在还不能确定


如果您能给出一个更具代表性的示例(包括您稍后提到的最后一个字段),我们可能会为您提供更多帮助。

应该首先调用超类构造函数-前面不能有任何语句,这是有意义的,因为在初始化对象之前必须实例化它


如果您无法通过重新设计应用程序来消除对它的需求,那么使用单独的方法解决此问题是可以接受的。

您提到了最终成员,因此我建议您以特殊顺序初始化它们。。。 但是如果我们看不到确切的问题,我们就不能给你公平的答案

无论如何,我只想指出,最终成员只能被分配到两个地方(据我所知)

  • 在你申报的地方
  • 或者在一个构造函数上下文中 任何将值分配给最终成员的其他尝试都将导致编译器错误


    我真的很想了解你问题的由来并帮助你。您能提供更多详细信息吗?

    什么
    final
    字段?您的代码中没有任何内容…请向我们展示一个完整的示例,说明您想要实现的目标。可能是
    init()
    代码有一些全局操作,这通常是有问题的(原因正是如此)。@Jim:我猜
    init1()
    实际上想要使用
    init2()
    中计算的一些值,这就是为什么我建议使用构造函数参数——但我们目前还不能确定。我不会说从构造函数调用虚拟方法是一个很好的解决方法。它可能偶尔是唯一可用的,但应尽可能避免使用。@Jon Skeet同意。我只需要将它与一些UI API一起使用,就可以在消息框类的文本之前插入任意对象。问题是,要实例化这些对象,我需要
    这个
    可用,这是一个恶性循环。我可能已经能够通过延迟初始化MessageBox类来解决这个问题(从而将那些方法调用移出)