Abstract Classes

  • Thread starter Christian Kreutzfeldt
  • Start date
C

Christian Kreutzfeldt

Hi folks!

Tonight I ran over a quite interesting behavior I
would have not expected like that. It might be kind
of stupid, but I cannot explain this behavior to me
and hope that someone of you can explain it to me.

I have three classes A, B and C which are connected
as follows:

---------- Class A ----------
public abstract class ClassA {

private String var = null;
public ClassA(String var) {
this.var = var;
}

public String getVar() {
return var;
}
}

---------- Class B ----------
public abstract class ClassB extends ClassA {

public ClassB(String var) {
super(var);
abstractMethod();
}

protected abstract void abstractMethod();

}

---------- Class C ----------
public class ClassC extends ClassB {

private String instanceVar = "predefined-value";

public ClassC(String var) {
super(var);
System.out.println("#constructor: var: " +
getVar() + ", instanceVar: " + instanceVar);
}

protected void abstractMethod() {
instanceVar = "new-value";
System.out.println("#abstractMethod: var: " +
getVar() + ", instanceVar: " + instanceVar);
}

}


If I do create an instance of
ClassC (new ClassC("test")) the output looks like this:

#abstractMethod: var: test, instanceVar: new-value
#constructor: var: test, instanceVar: predefined-value

But my expectation concerning the output was:
#abstractMethod: var: test, instanceVar: new-value
#constructor: var: test, instanceVar: new-value

Why does the setting of the instance variable of
ClassC in ClassC#abstractMethod does not survive the
constructor call?

First I thought that the instance variable would be
initialized at the end of the constructor call which
runs through all parents. But in that case the call
of abstractMethod in the constructor of ClassB must
have crashed since its implementation in ClassC
accesses the instance variable instanceVar.

Kind regards,
Christian Kreutzfeldt
 
P

Piotr Kobzda

Christian said:
Why does the setting of the instance variable of
ClassC in ClassC#abstractMethod does not survive the
constructor call?

Because an instance fields initialization takes place in the constructor
just after completion of a super class's constructor call.

Thus, your ClassC is effectively equivalent to the following:

public class ClassC extends ClassB {

private String instanceVar;

public ClassC(String var) {
super(var);
instanceVar = "predefined-value";
System.out.println("#constructor: var: " +
getVar() + ", instanceVar: " + instanceVar);
}

protected void abstractMethod() {
instanceVar = "new-value";
System.out.println("#abstractMethod: var: " +
getVar() + ", instanceVar: " + instanceVar);
}

}


That's one of the reasons to avoid instance methods invocations in the
constructors.


piotr
 
C

Christian Kreutzfeldt

Hi!
That's one of the reasons to avoid instance methods invocations in the
constructors.
Thanks for the hint. I had a similar assumption, but as I wrote, the
successful call of ClassC#abstractMethod from ClassB#constructor
that accessed the variable ClassC#instanceVar without any error
let me drop that idea. So, the variables are valid as soon as the
class get accessed but are fully initialized just _after_ the
constructor has finished.

Kind regards,
Christian Kreutzfeldt
 
C

Chris ( Val )

Hi!


Thanks for the hint. I had a similar assumption, but as I wrote, the
successful call of ClassC#abstractMethod from ClassB#constructor
that accessed the variable ClassC#instanceVar without any error
let me drop that idea. So, the variables are valid as soon as the
class get accessed but are fully initialized just _after_ the
constructor has finished.


A simple test:

abstract class Base {
public Base() {
System.out.println( "\nBase\n" );
printValue();
}

abstract void printValue();
}

class Derived extends Base {

public String value = "DAFAULT";

public Derived() {
System.out.println( "Derived: " + value + "\n" );
}

public void printValue() {
System.out.println( "printValue(): " + value + "\n" );
}
}


public class ConstructionTest
{
public static void main( String[] args ) {
Base b = new Derived();
}
}

<OUTPUT>
Base

printValue(): null

Derived: DAFAULT
</OUTPUT>

As you can see via the call to printValue(), the
instance variable named 'value' has yet to be
initialised (and is null), because Derived has
not yet been fully constructed.

Derived is fully constructed once its constructor
has completed, and the instance variable now holds
the value "DEFAULT".
 
L

Lew

Christian said:
Hi!

Thanks for the hint. I had a similar assumption, but as I wrote, the
successful call of ClassC#abstractMethod from ClassB#constructor
that accessed the variable ClassC#instanceVar without any error
let me drop that idea. So, the variables are valid as soon as the
class get accessed but are fully initialized just _after_ the
constructor has finished.

The JLS elucidates the details. For example, final instance variables are
(more) reliable. The guarantees, or lack thereof, are somewhat subtle.

The rule of thumb is to avoid non-construction activity in the constructor,
and another is not to call overridable methods from the constructor.
 
C

Chris ( Val )

The JLS elucidates the details. For example, final instance variables are
(more) reliable. The guarantees, or lack thereof, are somewhat subtle.

The rule of thumb is to avoid non-construction activity in the constructor,
and another is not to call overridable methods from the constructor.

I agree, but out of curiosity, can you think of an
example when polymorphic behaviour can be used via
a base class constructor?
 
D

Daniel Pitts

Christian said:
Hi folks!

Tonight I ran over a quite interesting behavior I
would have not expected like that. It might be kind
of stupid, but I cannot explain this behavior to me
and hope that someone of you can explain it to me.

I have three classes A, B and C which are connected
as follows:

---------- Class A ----------
public abstract class ClassA {

private String var = null;
public ClassA(String var) {
this.var = var;
}

public String getVar() {
return var;
}
}

---------- Class B ----------
public abstract class ClassB extends ClassA {

public ClassB(String var) {
super(var);
abstractMethod();
}

protected abstract void abstractMethod();

}

---------- Class C ----------
public class ClassC extends ClassB {

private String instanceVar = "predefined-value";

public ClassC(String var) {
super(var);
System.out.println("#constructor: var: " +
getVar() + ", instanceVar: " + instanceVar);
}

protected void abstractMethod() {
instanceVar = "new-value";
System.out.println("#abstractMethod: var: " +
getVar() + ", instanceVar: " + instanceVar);
}

}


If I do create an instance of
ClassC (new ClassC("test")) the output looks like this:

#abstractMethod: var: test, instanceVar: new-value
#constructor: var: test, instanceVar: predefined-value

But my expectation concerning the output was:
#abstractMethod: var: test, instanceVar: new-value
#constructor: var: test, instanceVar: new-value

Why does the setting of the instance variable of
ClassC in ClassC#abstractMethod does not survive the
constructor call?

First I thought that the instance variable would be
initialized at the end of the constructor call which
runs through all parents. But in that case the call
of abstractMethod in the constructor of ClassB must
have crashed since its implementation in ClassC
accesses the instance variable instanceVar.

Kind regards,
Christian Kreutzfeldt

This is one of the reasons it is technically incorrect to call an
overriddable method in the constructor. All methods called from the
constructor should either be private or final, unless the class itself
is final. What's more, is any methods that the constructor calls must
only call methods that are final or private. So on and so forth.

Basically, a the super-instance gets initialized first, so calling
something on the derived class is not valid. The exact rule is that a
super-class construction can not rely on the behavior of the sub-class
(except in the sense that a sub-class can choose a constructor).

Hope this clarifies things,
Daniel.
 
D

Daniel Pitts

Chris said:
I agree, but out of curiosity, can you think of an
example when polymorphic behaviour can be used via
a base class constructor?
It /can/ be used if the behavior isn't dependent on the state of the
derived object. It is a Bad Idea(TM) to do so. As Lew suggested, you
should limit the constructor to construction. If you need the base
constructors to behave differently, then have your subclass constructor
delegate to the appropriate one, or have a factory method on the
subclass that instantiates the object, and then calls "initialize" or
some such. *that* method can call polymorphic methods.
 
C

Chris ( Val )

Chris ( Val ) wrote:





It /can/ be used if the behavior isn't dependent on the state of the
derived object. It is a Bad Idea(TM) to do so. As Lew suggested, you
should limit the constructor to construction. If you need the base
constructors to behave differently, then have your subclass constructor
delegate to the appropriate one, or have a factory method on the
subclass that instantiates the object, and then calls "initialize" or
some such. *that* method can call polymorphic methods.

Hi Daniel,

I agree with Lew, and your analysis :)

I guess I was looking for a specific example. The C++ language
allows such calls in the constructor too, but I have yet to see
something achieved in this way that couldn't be achieved with a
different design.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top