P45 Imminent
P45 Imminent

Reputation: 8591

How to call super() when referring to a member of the child class

I have a parent class

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

And a child class

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());
    }
}

Note that ChildDatabase implements java.lang.AutoCloseable which is a reason why I need to keep a ChildDatabase reference in the Child class. Note that ChildDatabase extends Database.

The problem is that super(m_d = CreateDatabase()); is not valid since you can't refer to child class members in the call to super.

Is there a syntax I'm missing which would allow me to do this? An alternative would be to implement a getDatabase() method in Parent, and cast that result to a ChildDatabase to store as m_d;, but that seems a smell to me.

Note that CreateDatabase can be static.

Upvotes: 2

Views: 103

Answers (4)

davidxxx
davidxxx

Reputation: 131346

By introducing a getter of the object in the parent class you could write it :

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();
    }
}

But the downcast is undesirable, it may fail at runtime and it looks a hack.
If the subclass needs to refer to the field as a ChildDataBase, it should be explicit in the constructor itself such as :

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

In a general way dependencies should be explicit if not an implementation detail since It favors the testability and the switch to other implementations.

Note that I don't like either a lot this solution.
This still duplicates the field in the hierarchy.
The problem here is that you want to value the same object with for the parent constructor and the child constructor a distinct declared type.
You should not. It is error prone for people reading your code.
Using a generic class is probably better and would allow subclasses to specify the subclass for Database fields.
It will still require to provide the getter in the parent class.

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

With hidden dependency :

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

With exposed dependency :

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

Upvotes: 0

Prasad Karunagoda
Prasad Karunagoda

Reputation: 2148

Why do you want to have the field m_d in both Parent and Child? Isn't it sufficient to have it only in Parent? And simply have super(createDatabase()); in Child's constructor like in below code:

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
  {
  }
}

Upvotes: 0

tmarwen
tmarwen

Reputation: 16354

What you are trying to achieve is simply not feasible.

Following the Java language specification, no class field member can be initialized before the call to super (constructor) has been done.

As you are implementing a specific behavior within your Child class, a workaround, may be to omit the local m_d field passing the desired Database instance reference over a factory mixed with a generic formal type for the sub-type Database.

Here down the Parent class type:

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

    Parent(D m_d) {
        m_d = m_d;
    }

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

And the Child would be as follows:

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());
        }
    }
}

Upvotes: 0

8192K
8192K

Reputation: 5290

How about

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

    Parent()
    {
        m_d = getDatabase();
    }

    abstract DB getDatabase();
}

and

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

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

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

Then you can even use AutoCloseable features directly on this.m_d (like try...with etc).

Upvotes: 2

Related Questions