考虑一下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类来解决这个问题(从而将那些方法调用移出)