引用子类成员时如何调用super()

时间:2019-02-01 14:56:02

标签: java

我有一个家长班

public class Parent
{
    Database m_d;
    Parent(Database d){
        m_d = d;
    }
}

还有一个儿童班

public class Child extends Parent implements java.lang.AutoCloseable
{
    ChildDatabase m_d
    Child()
    {
        // Error on the next statement:
        // "Cannot reference m_database before supertype constructor
        // has been called"
        super(m_d = CreateDatabase());
    }
}

请注意,ChildDatabase实现了java.lang.AutoCloseable,这就是为什么我需要在ChildDatabase类中保留Child引用的原因。请注意,ChildDatabase扩展了Database

问题在于super(m_d = CreateDatabase());无效,因为您无法在对super的调用中引用子类成员。

是否缺少我可以使用的语法?一种替代方法是在getDatabase()中实现Parent方法,并将结果转换为ChildDatabase并存储为m_d;,但这对我来说似乎是一种气味。 >

请注意,CreateDatabase可以是static

4 个答案:

答案 0 :(得分:2)

怎么样

public abstract class Parent<DB extends Database>
{
    DB m_d;

    Parent()
    {
        m_d = getDatabase();
    }

    abstract DB getDatabase();
}

public class Child extends Parent<ChildDatabase> implements java.lang.AutoCloseable
{

    Child()
    {
        // do something with this.m_d here
    }

    @Override
    ChildDatabase getDatabase()
    {
        return createChildDatabase();
    }
}

然后,你甚至可以使用AutoCloseable功能,直接在this.m_d(如试...用等)。

答案 1 :(得分:0)

通过在父类中引入对象的吸气剂,您可以编写它:

public class Child extends Parent implements java.lang.AutoCloseable
{
    ChildDatabase m_d;
    Child()
    {
        // Error on the next statement:
        // "Cannot reference m_database before supertype constructor
        // has been called"
        super(CreateDatabase());
        this.m_d = (ChildDatabase)getMd();
    }
}

但是向下转换是不可取的,它可能会在运行时失败并且看起来很hack。
如果子类需要将字段引用为ChildDataBase,则它应该在构造函数本身中是显式的,例如:

ChildDatabase m_d;
Child(ChildDatabase childDb)
{
    super(childDb);
    m_d = childDb;
}

通常,如果不是实现细节,则应明确依赖项,因为它有利于可测试性和转换为其他实现。

请注意,我对此解决方案也不满意。
这仍然会复制层次结构中的字段。
这里的问题是,您想为父构造函数和子构造函数赋值不同的已声明类型的同一对象。
你不应该。人们阅读您的代码时容易出错。
使用通用类可能更好,并且允许子类为数据库字段指定子类。
仍然需要在父类中提供getter。

public class Parent<T ? extends DataBase>
{
    private T m_d;
    Parent(T d){
        m_d = d;
    }
    public T getMd(){
      return m_d;
    }
}

具有隐藏依赖性:

public class Child extends Parent<ChildDataBase> implements AutoCloseable
{
  Child(){
    super(CreateDatabase());      
  }
  void foo(){
     ChildDataBase md = getMd(); // return the generic type
  }
}

具有公开的依赖性:

public class Child extends Parent<ChildDataBase> implements AutoCloseable
{
  Child(ChildDataBase md){
    super(md);      
  }
  void foo(){
     ChildDataBase md = getMd(); // return the generic type
  }
}

答案 2 :(得分:0)

您要实现的目标只是不可行

Java language specification之后,在完成对super(构造函数)的调用之前,无法初始化任何类字段成员。

当您在Child类中实现特定行为时,一种解决方法可能是忽略本地m_d字段,该字段在与工厂混合的工厂中传递所需的Database实例引用。子类型Database的通用形式类型。

下面是Parent类类型:

public class Parent<D extends Database> {
    D m_d;

    Parent(D m_d) {
        m_d = m_d;
    }

    protected D getDatabase() {
        return this.m_d;
    }
}

Child如下:

public class Child extends Parent<ChildDatabase> {

    private Child(ChildDatabase m_d) {
        super(m_d);
    }

    private static ChildDatabase createDatabase() {
        return null;
    }

    public static class ChildFactory {

        public static Child createChild() {
            return new Child(createDatabase());
        }
    }
}

答案 3 :(得分:0)

为什么要在m_dParent中都包含字段Child?仅在Parent中使用它是否足够?只需将super(createDatabase());的{​​{1}}放在Child的构造函数中,如以下代码所示:

public class Parent
{
  Database m_d;

  Parent(Database d)
  {
    m_d = d;
  }
}

class Child extends Parent implements java.lang.AutoCloseable
{
  Child()
  {
    super(createDatabase());
  }

  private static ChildDatabase createDatabase()
  {
    return new ChildDatabase();
  }

  @Override
  public void close() throws Exception
  {
  }
}

class Database
{
}

class ChildDatabase extends Database implements java.lang.AutoCloseable
{
  @Override
  public void close() throws Exception
  {
  }
}