calling own methods from constructor

  • Thread starter Andreas Leitgeb
  • Start date
R

Robert Klemme

Because getClass() is final, it didn't occur to me, that he
would have put up *that* as an example...

Now there we have something *I* have overlooked. :) I thought he had
picked it because getClass() always returns the actual class of the
instance regardless from which constructor it is invoked (other than
in C++) - but yes, it's probably not the perfect example.
As for me, I can't remember having shot myself in the foot with this
feature of Java (i.e. allowing to invoke virtual methods in
constructor).  I reckon, language designers figured that allowing it
is more worthwhile than preventing it.  As Thomas pointed out you
would have to provide constructor arguments so subclasses can pass on
data they have created.  This is tiresome and may even end up being
inefficient namely in the case where the superclass constructor needs
to decide which methods to call (or whether methods to call).  You
would end up creating objects which then need to be discarded if the
super class constructor decides that he does not need them =>
inefficient code.

The example of that Library class-hierarchy looked a bit
contrived to me.
Btw, the check would be expensive for the compiler and I am also not
sure how that byte code might look like because you need to extend the
restrictions to all methods. [...]

It would be already an improvement, if direct calls to such
methods from the constructor were forbidden.

I am not convinced. The risk seems rather low compared to the efforts
needed to implement your rule and the fact that now suddenly we get an
inconsistency: direct calls are forbidden while indirect calls are
allowed. Everybody needing this would instantaneously would start
swearing and a second later introduce a private method whose only
purpose is to delegate the call to the "forbidden" method. No, I
don't think this is a good idea. After all we are grown ups and
should take responsibility for the code we write. How often did you
shoot yourself in the foot because you are allowed to invoke sub class
methods from a constructor?

Cheers

robert
 
L

Lew

Because getClass() is final, it didn't occur to me, that he
would have put up *that* as an example...

Boy, you're spoiling for a fight, ain't'cha, sport?

It's final but it always returns the runtime class, so it is
overridden by the individual subtypes under the hood by special
compiler magic. Otherwise 'String#getClass()' would report type
'Object', which obviously it doesn't.

It presents the same kind of behavior as non-final overridable methods
for the purposes of this discussion.

You can use the same idiom using the "true Scotsman" definition of
overridability, just so we don't get sidetracked in a pointless
quibble. Hm-kaay?

(uncompiled:)

class Foo
{
private static final String FOOT = Foo.class.getSimpleName();
public String getFoot()
{
return FOOT;
}

private final String represent = "Insert "+ getFoot();
public String toString()
{
return represent;
}
}

class Bar extends Foo
{
private static final String BART = Bar.class.getSimpleName();
public String getFoot()
{
return BART;
}
}
 
J

Jim Janney

Owen Jacobson said:
It's hard to prove that a constructor never calls a virtual method. Consider:

public class Foo {
public Foo() {
// internalInit is private, therefore final
this.internalInit();
}

public /* virtual */ void virtualMethod() {
System.out.println("Override me! I dare you.");
}

private void internalInit() {
// Whups! 'this' is not always fully initialized.
this.virtualMethod();
}
}

If you forbid internalInit from calling virtual methods because it is,
itself, called from a constructor, you also prevent it from calling
virtual methods when called from a normal method. If you don't prevent
that, but do prevent Foo's constructor from calling any of its own
virtual methods, then you end up with the question "why does Java make
me use a private method when I want to call a virtual method from a
constructor?" instead.

public class Bar extends Foo {
public Bar() {
super();
System.out.println("Two guys walk into a...");
}

@override
public void virtualMethod() {
throw new Exception("the bar is closed");
}
}

In Java evaluating new Bar() will throw an exception. But in C++ the
equivalent code would print

Override me! I dare you.
Two guys walk into a...

In effect, until the constructor of Foo completes, the object is
considered to be an instance of Foo, so calls to virtualMethod() to to
Foo.virtualMethod even if it has been overridden. After the super
constructor completes, the object is treated as an instance of Bar, so
evaluating new Bar().virtualMethod() would print two lines and then
throw an exception.

I've been surprised by this behaviour in C++ enough times that I'm not
sure that it has the better approach. But a solution does exist.
 
A

Alessio Stalla

Casting in C++ is something different than in Java.
Although, if you really do C, not C++, then it's it's
much more like Java, except for the lacking safety net.

I don't know about C++, but in C casting is not like Java at all. In
Java a cast is a runtime operation that checks the type of some object
(I'm not considering primitives). Of course the compiler knows about
it and uses it at compile-time too for type checking, but that's
somewhat a consequence of the previous point. In C, instead, casting
has no(*) runtime behavior; it's just an instruction for the compiler:
"please consider this datum to be of this type". If the compiler
accepts your order, it will blindly treat that datum as if it's of the
type you told it is, even if at runtime it's not.

(*) actually, it might perform conversions in certain corner cases,
but still no runtime type check is ever done.
 
T

Tobias Blass

I don't know about C++, but in C casting is not like Java at all. In
Java a cast is a runtime operation that checks the type of some object
(I'm not considering primitives). Of course the compiler knows about
it and uses it at compile-time too for type checking, but that's
somewhat a consequence of the previous point. In C, instead, casting
has no(*) runtime behavior; it's just an instruction for the compiler:
"please consider this datum to be of this type". If the compiler
accepts your order, it will blindly treat that datum as if it's of the
type you told it is, even if at runtime it's not.

(*) actually, it might perform conversions in certain corner cases,
but still no runtime type check is ever done.
Is there any case where the C compiler rejects casts? I cannot imagine an
example.
 
A

Andreas Leitgeb

I dare to disagree, but this group here is not the place to elaborate
on it. In a nutshell: C++ casts are just so much more unlike Java's ...
Is there any case where the C compiler rejects casts? I cannot imagine an
example.

Most likely it will reject casts between different structures or between
structures and primitives. (too lazy to test this now, though)
 
A

Andreas Leitgeb

Jim Janney said:
In Java evaluating new Bar() will throw an exception. But in C++ the
equivalent code would print
Override me! I dare you.
Two guys walk into a...

I think, C++'s semantics are *better* for this case, but probably at
a price, that made it reasonable for Java to find a different tradeoff.
 
A

Andreas Leitgeb

Lew said:
Andreas Leitgeb wrote:
Is there any *good* use of having the constructor call a method that
actually *can* be overridden in a subclass? ...
Foo() { [...] = [...] someStaticMethod ( getClass() ); [...] }
Because getClass() is final, it didn't occur to me, that he
would have put up *that* as an example...
Boy, you're spoiling for a fight, ain't'cha, sport?
It's final but it always returns the runtime class, so it is
overridden by the individual subtypes under the hood by special
compiler magic.

Actually, I tried to explain, why I was rambling about static methods
in response to your example.

Now, that you've made your point clearer, namely that getClass()
behaves like a virtual method overridden for *every* class, I even
understand it.

I'm just not yet convinced that this particular pattern, which is already
handled by JVM-magic (rather than by the compiler synthesizing those
virtual methods), would say "it's good to be able to call overridable
non-static methods from a constructor" sufficiently loudly. ;-)
 
J

Jim Janney

Casts are routinely used to force conversions where needed:

#include <stdio.h>

int main() {
int i = 17;
printf("%f\n", (double) i);
return 0;
}
Is there any case where the C compiler rejects casts? I cannot imagine an
example.

struct {
char* s;
double d;
} st;

int i = (int) st; /* error: aggregate value used where an integer was expected */
 
L

Lew

Janney said:
In Java evaluating new Bar() will throw an exception.  But in C++ the
equivalent code would print

Override me! I dare you.
Two guys walk into a...

In effect, until the constructor of Foo completes, the object is
considered to be an instance of Foo, so calls to virtualMethod() to to
Foo.virtualMethod even if it has been overridden.  After the super
constructor completes, the object is treated as an instance of Bar, so
evaluating new Bar().virtualMethod() would print two lines and then
throw an exception.

I've been surprised by this behaviour in C++ enough times that I'm not
sure that it has the better approach.  But a solution does exist.

A solution to what, exactly?

I ask because it sort of sounds like you're saying that C++ is a
solution to something in Java. I don't guess that's what you're
really saying, of course, but it leaves me wondering what that is.
What is the problem that you aver exists, and what is its solution to
it that you aver exists?
 
L

Lew

Andreas said:
Actually, I tried to explain, why I was rambling about static methods
in response to your example.

No, you tried to be sarcastic and to make my answer seem wrong even
though it was precisely what you asked for.
Now, that you've made your point clearer, namely that getClass()
behaves like a virtual method overridden for *every* class, I even
understand it.

And it even exactly fits the parameters of your original question.
I'm just not yet convinced that this particular pattern, which is already
handled by JVM-magic (rather than by the compiler synthesizing those
virtual methods), would say "it's good to be able to call overridable
non-static methods from a constructor" sufficiently loudly. ;-)

I didn't ever say that it was good in such a general sense, thank you
very much for shifting the context yet again.

You asked if there were specific cases where it was good to use an
overridable (you never said 'final' until you started pulling your "no
true Scotsman" routine) method. I pointed out such.

"Loudness" was not part of your request.

It's just that there are occasionally (!) times when an overridable
method (though not necessarily a 'final' one, since even 'final'
methods can call overridable methods in turn) can be usefully called
inside a constructor (or initializer, equivalently). That was the
parameter of the original question, and that has been demonstrated.

Twice over, now.
 
T

Tom Anderson

public class Bar extends Foo {
public Bar() {
super();
System.out.println("Two guys walk into a...");
}

@override
public void virtualMethod() {
throw new Exception("the bar is closed");
}
}

In Java evaluating new Bar() will throw an exception. But in C++ the
equivalent code would print

Override me! I dare you.
Two guys walk into a...

In effect, until the constructor of Foo completes, the object is
considered to be an instance of Foo, so calls to virtualMethod() to to
Foo.virtualMethod even if it has been overridden. After the super
constructor completes, the object is treated as an instance of Bar, so
evaluating new Bar().virtualMethod() would print two lines and then
throw an exception.

I recoil in horror.
I've been surprised by this behaviour in C++ enough times that I'm not
sure that it has the better approach. But a solution does exist.

That's slightly like solving toothache by amputating your head.

tom
 
T

Tom Anderson

Boy, you're spoiling for a fight, ain't'cha, sport?

It's final but it always returns the runtime class, so it is overridden
by the individual subtypes under the hood by special compiler magic.

What? No.
Otherwise 'String#getClass()' would report type 'Object', which
obviously it doesn't.

No. getClass() is final, not virtual, it is never overridden, and it does
not behave like it has been overridden. Rather, it behaves like its
implementation was:

public Class getClass() {
return this.class;
}

Where all objects have a secret magic hidden field called 'class' which is
set by the VM when they are allocated.
It presents the same kind of behavior as non-final overridable methods
for the purposes of this discussion.

It really doesn't. The problem with calling overridable methods from
constructors is that they can be overridden with code which assumes a
constructed object, and gets into trouble when run in the context of one
which isn't. getClass() can't get into that kind of trouble.

tom
 
T

Tom Anderson

The example of that Library class-hierarchy looked a bit
contrived to me.

It was entirely contrived. I will keep an eye out for real code which does
this, but i thought a timely answer was desirable!

tom
 
A

Andreas Leitgeb

Lew said:
No, you tried to be sarcastic and to make my answer seem wrong even
though it was precisely what you asked for.

Without the explanation that you only gave later, it *was* wrong.

I asked for an example of calling an overridable method from the
constructor, and you gave an example where both a static and a final
method got called. That that final method *could* have been instead
implemented as a bunch of virtual methods, definitely wasn't obvious
at that point.
If you provide cryptic answers, just don't be surprised about them
being misunderstood and therefore taken as non-answers. Until you
explain them, that is.
You asked if there were specific cases where it was good to use an
overridable (you never said 'final' [...])

But 'final' surely implies non-overridability, doesn't it? Therefore,
overridability of a method implies its non-'final'ity.
It's just that there are occasionally (!) times when an overridable
method (though not necessarily a 'final' one, since even 'final'
methods can call overridable methods in turn) can be usefully called
inside a constructor (or initializer, equivalently). That was the
parameter of the original question, and that has been demonstrated.
Twice over, now.
'kay
 
J

Jim Janney

Lew said:
A solution to what, exactly?

In the message I was replying to, in the text that you deleted, Owen
Jacobsen correctly observed that

This is a technical issue, and I observed that other languages have
found ways to prevent virtual methods from being called before their
owning objects have been fully constructed. One can debate whether this
desirable, but C++ provides an existence proof that it's possible.
I ask because it sort of sounds like you're saying that C++ is a
solution to something in Java. I don't guess that's what you're
really saying, of course, but it leaves me wondering what that is.
What is the problem that you aver exists, and what is its solution to
it that you aver exists?

Consider focussing on what I did say, instead of what it sort of sounds
like I might have said. The solution is in the paragraph above, the one
that starts with "In effect, until the constructor of Foo completes".
 
J

Jim Janney

Tom Anderson said:
I recoil in horror.

It's surprised me more than once. You'd think after the first time I
would remember, but no...
That's slightly like solving toothache by amputating your head.

C++ is an interesting language. The interesting languages aren't always
the ones you want to do actual work in.
 
D

Daniele Futtorovic

There is well-known danger in calling own methods from the
constructor, namely that the method called may be overridden by a
subclass, which is really instanciated, but whose specific
constructor has not yet been run.

Definitely present.
Is there any *good* use of having the constructor call a method that
actually *can* be overridden in a subclass? I mean, are there
(non-anti)patterns of explicitly allowing subclasses to hook into
base-class's construction?

I've recently written something like this:

class BaseClass implements java.io.Externalizable {
protected Map<?, ?> store;
protected BaseClass( Map<?, ?> store ){
this.store = store;
initialise0();
}
public BaseClass(){ } //needed for Externalizable

protected void initialise0(){}

public void readExternal( ObjectInput input ){
this.store = (Map<?, ?>) input.readObject();
initialise0();
}
}

class SubClass {
private static final Object KEY = ...
String datum;
public SubClass( Map<?, ?> store ){
super( store );
}

protected void initialise0(){
super.initialise0();
datum = (String) store.get( KEY );
}
}

I consider this a valid use. Sure, even at this basic level you need to
be careful, especially if you get back to the code after a while and
start extending the hierarchy.

I could have duplicated the initialisation code in the (Map<?,?>) c'tor.
I'm not very fond of big c'tors however, and even less fond of code
duplication. So there.

As for the fact that you can call virtual methods, I'm all for it, just
as I am against any sensible restriction. We're all adults (probably)
and know what we're doing (mostly), and anyway there's already so many
more places you can screw up if you're not careful (autoboxing comes to
mind) -- I generally pay a lot more attention when I craft a class
hierarchy than when I write a single statement involving primitives and
Objects at the same time.
 
D

Daniele Futtorovic

class BaseClass implements java.io.Externalizable {
protected Map<?, ?> store;
protected BaseClass( Map<?, ?> store ){
this.store = store;
initialise0();
}
public BaseClass(){ } //needed for Externalizable

protected void initialise0(){}

public void readExternal( ObjectInput input ){
this.store = (Map<?, ?>) input.readObject();
initialise0();
}
}

class SubClass {
private static final Object KEY = ...
String datum;
public SubClass( Map<?, ?> store ){
super( store );
}

protected void initialise0(){
super.initialise0();
datum = (String) store.get( KEY );
}
}

'class SubClass *extends BaseClass*', of course.
 

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,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top