12

In a Java program, I have multiple subclasses inheriting from a parent (which is abstract). I wanted to express that every child should have a member that is set once only (which I was planning to do from the constructor). My plan was to code s.th. like this:

public abstract class Parent {
    protected final String birthmark;
}

public class Child extends Parent {
    public Child(String s) {
        this.birthmark = s;
    }
}

However, this seems to not please the Java gods. In the parent class, I get the message that birthmark "might not have been initialized", in the child class I get "The final field birthmark cannot be accessed".

So what's the Java way for this? What am I missing?

Hanno Fietz
  • 30,799
  • 47
  • 148
  • 234
  • Not a direct answer, but in the class file format final instance fields can only be set by the same class file but not necessarily in the constructor. IIRC. – Tom Hawtin - tackline Mar 19 '09 at 18:51
  • Related question: http://stackoverflow.com/questions/14383276/initialize-member-of-abstract-class-without-subclasses-having-write-access – user905686 Jun 03 '13 at 17:20

8 Answers8

20

You can't do it because while comparing the parent class, the compiler can't be sure that the subclass will initialize it. You'll have to initialize it in the parent's constructor, and have the child call the parent's constructor:

public abstract class Parent {
    protected final String birthmark;
    protected Parent(String s) {
        birthmark = s;
    }
}

public class Child extends Parent {
    public Child(String s) {
        super(s);
        ...
    }
}
Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • If I would skip declaring the child's constructor (and, in that case, probably keep the parent's ctor public), I could do 'new Child("set this")' and get what I want, right? – Hanno Fietz Mar 19 '09 at 18:55
7

Pass it to the parent constructor:

public abstract class Parent {
    private final String birthmark;
    public Parent(String s) {
        birthmark = s;
    }
}

public class Child extends Parent {
    public Child(String s) {
        super(s);
    }
}
McDowell
  • 107,573
  • 31
  • 204
  • 267
2

Another Java-ish way to do this is probably to have the parent class to define an abstract "getter", and have the children implement it. It's not a great way to do it in this case, but it in some cases it can be exactly what you want.

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
  • calling the abstract getter method in the constructor can be potentially dangerous however, a good rule is to never call an overrideable method, either directlry or indirectly, from a constructor. – TofuBeer Mar 20 '09 at 00:30
  • @TofuBeer any example how dangerous that could be? this answer will work perfectly for me [here](http://stackoverflow.com/questions/34822434/composite-inheritance-how-to-assign-a-final-field-at-sub-class-constructor-whic)! EDIT [mmm](http://stackoverflow.com/questions/3404301/whats-wrong-with-overridable-method-calls-in-constructors) – Aquarius Power Jan 16 '16 at 02:42
  • 1
    If you call an overridden method in a constructor, and that method accesses a variable from the child class then that variable will not yet be initialized because the constructor of the child class has not been instantiated yet. It is dangerous because it may work now, but if someone changes the getter later on to do that it will crash or get the wrong value. – TofuBeer Jan 16 '16 at 05:27
2

I would do it like this:

public abstract class Parent 
{
    protected final String birthmark;

    protected Parent(final String mark)
    {
        // only if this makes sense.
        if(mark == null)
        {
            throw new IllegalArgumentException("mark cannot be null");
        }

        birthmark = mark;
    }
}

public class Child 
    extends Parent 
{
    public Child(final String s) 
    {
        super(s);
    }
}

final means that the variable can be initialized once per instance. The compiler isn't able to make sure that every subclass will provide the assignment to birthmark so it forces the assignment to happen in the constructor of the parent class.

I added the checking for null just to show that you also get the benefit of being able to check the arguments in one place rather than each cosntructor.

Hanno Fietz
  • 30,799
  • 47
  • 148
  • 234
TofuBeer
  • 60,850
  • 18
  • 118
  • 163
  • Is there a reason to mark the parameters in the declaration of the function final, too? The compiler let me get away without. – Hanno Fietz Mar 19 '09 at 20:10
  • I do it out of habit... stops you from doing param = instanceVariable. If hotspot is ever able to optimize final vars better (I don't think it does now) then my code will magically be faster too :-) – TofuBeer Mar 19 '09 at 20:23
1

Why not delegate initialization to a method. Then override the method in the parent class.

public class Parent {
   public final Object x = getValueOfX();
   public Object getValueOfX() {
      return y;
   }
}
public class Child {
  @Override
  public Object getValueOfX() {
     // whatever ...
  }
}

This should allow custom initialization.

ng.
  • 7,099
  • 1
  • 38
  • 42
  • 1
    This idiom is a really bad idea... if a user makes getValueofX() depend on any field of the Child class, there will be a NullPointerException, since the Child has not yet actually been constructed. It can work if you are very careful with docs, but stupid/malicious users can break it in a heartbeat. – Art Doler Mar 19 '09 at 18:57
  • @Chamelaeon: This is such a stupid comment, what would a malicious or stupid user be doing writing code for your project? A malicious or stupid user could write System.exit(0)? – ng. Mar 20 '09 at 09:32
  • this will help [here](http://stackoverflow.com/questions/34822434/composite-inheritance-how-to-assign-a-final-field-at-sub-class-constructor-whic), thx! – Aquarius Power Jan 16 '16 at 02:48
0

Yes, the final members are to be assigned in the class in which they are declared. You need to add a constructor with a String argument to Parent.

Maurice Perry
  • 32,610
  • 9
  • 70
  • 97
0

Declare a constructor in the superclass that's called by the subclass. You must set the field in the superclass to make sure it's initialized, or the compiler can't be sure the field is initialized.

Jorn
  • 20,612
  • 18
  • 79
  • 126
0

You probably want to have a Parent(String birthmark) constructor so that you can ensure in your Parent class that final is always initialized. Then you can call super(birthmark) from your Child() constructor.

Cody Casterline
  • 787
  • 5
  • 11