【问题标题】:Calling method from constructor从构造函数调用方法
【发布时间】:2013-08-10 21:30:33
【问题描述】:

请原谅任何小的语法错误或诸如此类的问题,我正在使用 Jitsi 模块遇到这种情况,并且对 Java 不太熟悉,想确认发生了什么以及为什么以及如何修复它。

 public abstract class A
{
  public A()
  {
    this.load();
  }

  protected void load()
  {

  }
}

public class B extends A
{
  private String testString = null; 

  public B()
  {
    super();
  }

  @Override
  protected void load()
  {
    testString = "test";
  }
}

应用程序在使用按名称加载类的方法创建类 B 的实例时这样做:

  • 在 B 类中调用重写的 load()
  • 初始化变量(根据调试器调用“private string testString = null”),将它们清空。

这是预期的 Java 行为吗?什么可能导致这种情况?它是在 1.7 JDK 上运行的 Java 1.6 应用程序。

【问题讨论】:

    标签: java class constructor


    【解决方案1】:

    这是预期的 Java 行为吗?

    是的。

    是什么原因造成的?

    您在非最终超类构造函数中调用非最终覆盖方法。

    让我们一步一步来看看会发生什么:

    • 您创建了一个B 的实例。
    • B() 调用超类构造函数 - A(),初始化超类成员。
    • A() 现在调用一个非 final 方法,该方法在 B 类中被覆盖,作为初始化的一部分。
    • 由于上下文中的实例属于B 类,所以调用的方法load() 属于B 类。
    • load() 初始化 B 类实例字段 - testString
    • 超类构造函数完成工作并返回(假设构造函数链接到Object类已经完成)
    • B() 构造函数开始进一步执行,初始化它自己的成员。
    • 现在,作为初始化过程的一部分,B 会覆盖testString 中先前写入的值,并将其重新初始化为null

    道德:永远不要在其构造函数中调用非最终类的非最终公共方法。

    【讨论】:

    • 太棒了,谢谢。我将研究项目的其他部分是如何运作的,他们必须做得正确,这是异常值,所以我将关注应用程序的其余部分。
    • 这是否意味着如果我们确实从构造函数调用实例方法,我们正在调用半熟对象的方法?
    【解决方案2】:

    这是构造时初始化的常见问题模式,经常出现在基础架构代码和自制 DAO 中。

    对“null”的赋值是不需要的,可以删除。

    如果这还不足以作为一个快速补丁,那么: 将所有后期构造 init 移动到一个单独的方法中,并将其全部包装在一个“静态方法”伪构造函数中。

    如果你在做 DAO 的事情,区分“加载”和“创建”真的很好,因为它们是完全不同的实例化。为这些定义单独的“静态构造函数”方法,也许还有单独的内部初始化。

    abstract public class A {
        protected void initAfterCreate() {}
    }
    
    public class B {
    
        @Override
        protected void initAfterCreate() {
            this.testString = "test";
        }
    
        // static constructors;
        //     --        
        static public B createB() {
            B result = new B();
            result.initAfterCreate();
        }
    }
    

    演示 DAO 的加载/创建分离:

    public class Order {
        protected int id;
        protected boolean dbExists;
    
        static public load (int id) {
            Order result = new Order( id, true);
            // populate from SQL query..
            return result;
        }
        static public create() {
            // allocate a key.
            int id = KeyAlloc.allocate( "Order");
            Order result = new Order( id, false);
        }
    
        // internal constructor;  not for external access.
        //
        protected Order (int id, boolean dbExists) {
            this.id = id;
            this.dbExists = dbExists;
        }
    }
    

    【讨论】:

    • load() 是模块通常用来从 .properties 文件中提取其配置的方法,我已经接触了 Jitsi 开发邮件列表的基础,我们将看看他们推荐的方法是什么为了保持代码的一致性(并非所有模块都加载属性,而且我厌倦了寻找......无论如何都必须在他们的列表中提交错误)。
    • 好的,所以这不是 DAO 的东西。这将使它更简单——那么应该只是一个初始化路径。 initAfterCreate() 是唯一基本可靠的解决方案,最好将其包装在静态工厂方法中。
    • 为什么要投反对票?分享了很多初始化构造缠结的实际经验——它们出现的实际情况,以及解决它们的有效模式(“静态构造函数”)。
    猜你喜欢
    • 2016-05-31
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 2015-10-10
    • 2012-09-16
    • 2015-10-01
    • 2011-10-22
    • 1970-01-01
    相关资源
    最近更新 更多