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.
I do not intend to delve into the details of static, private
or final methods, or final'ity of the class itself (and maybe
others) avoiding these problems, but instead I'm curious, why
Java just doesn't simply forbid the dangerous calls.
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?
--- sscce Test.java begin ---
public class Test {
Test() { foo(); }
void foo() { }
}
--- sscce Test.java end ---
PS: I know that this is not the only spot where Java does let one
shoot in one's feet. But unlike other situations, this one just
seems so easy to detect from static analysis.
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/6/2011 8:48:41 PM |
|
On Wed, 6 Apr 2011, Andreas Leitgeb wrote:
> 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?
public abstract class Library {
private List<Document> documents;
protected Library() {
documents = new ArrayList<Document>();
Collection<String> titles = listDocuments();
for (String title: titles) {
Document doc = loadDocument(title);
// do other preparatory stuff with the document
documents.add(doc);
}
}
protected abstract Collection<String> listDocuments();
protected abstract Document loadDocument(String title);
}
public class FilesystemLibrary extends Library {
// ...
}
public class WebDavLibrary extends Library {
// ...
}
public class JCRLibrary extends Library {
// ...
}
What are the alternatives?
The obvious one is for the subclass constructor to prepare all the objects
and pass them upward; i think that is likely to lead to a lot of
duplication of effort.
The almost as obvious one is to push the abstract methods out into a
separate interface - DocumentStore, say - and have the subclass
constructor pass up an instance of that.
You could also push the repeated logic out into some sort of factory or
helper, and have the subclasses call that, rather than relying on code in
the supeclass, but that is repetitive, and does nothing to establish
invariants in the superclass.
tom
--
Science which is distinguishable from magic is insufficiently advanced
|
|
0
|
|
|
|
Reply
|
twic (2083)
|
4/6/2011 10:06:38 PM
|
|
Andreas Leitgeb wrote:
>> 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?
import org.apache.log4j.Logger;
import static org.apache.log4j.getLogger;
public class Foo
{
private final Logger logger = getLogger( getClass() );
// ...
}
--
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
|
|
0
|
|
|
|
Reply
|
noone7 (3512)
|
4/6/2011 10:24:29 PM
|
|
On 4/6/2011 6:06 PM, Tom Anderson wrote:
> On Wed, 6 Apr 2011, Andreas Leitgeb wrote:
>
>> 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?
>
> public abstract class Library {
> private List<Document> documents;
>
> protected Library() {
> documents = new ArrayList<Document>();
> Collection<String> titles = listDocuments();
> for (String title: titles) {
> Document doc = loadDocument(title);
> // do other preparatory stuff with the document
> documents.add(doc);
> }
> }
>
> protected abstract Collection<String> listDocuments();
> protected abstract Document loadDocument(String title);
> }
>
> public class FilesystemLibrary extends Library {
> // ...
> }
>
> public class WebDavLibrary extends Library {
> // ...
> }
>
> public class JCRLibrary extends Library {
> // ...
> }
>
> What are the alternatives?
Safer ones, I hope. This code presupposes that the subclass
instance can do useful work before its constructor finishes -- to
put it another way, it assumes that the subclass constructor does
absolutely nothing except call the superclass constructor (and even
that much requires some leaps of faith).
How might Java ensure such an assumption? I can't imagine how
it could be done at compile time, when the suite of subclasses may
not even exist to be inspected. At run time, I guess it could be
done with two additional bits per instance: One that says "The
constructor has not yet returned" and another that says "A virtual
method has been called while the constructor is active." Each
virtual method would copy the first bit to the second, and if the
constructor found the second bit set while doing anything other
than a "return," it could throw an exception.
> The obvious one is for the subclass constructor to prepare all the
> objects and pass them upward; i think that is likely to lead to a lot of
> duplication of effort.
>
> The almost as obvious one is to push the abstract methods out into a
> separate interface - DocumentStore, say - and have the subclass
> constructor pass up an instance of that.
>
> You could also push the repeated logic out into some sort of factory or
> helper, and have the subclasses call that, rather than relying on code
> in the supeclass, but that is repetitive, and does nothing to establish
> invariants in the superclass.
It seems to me the constructor is doing too much of the heavy
lifting. A Library(Collection<Document>) constructor, with the
Documents already loaded or maybe with "load on first use" flags,
seems a more tenable approach. In particular, it allows the
subclass constructors to choose their own sets of exceptions to
throw, instead of requiring that they all extend exceptions thrown
by the superclass' abstract method declarations.
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
esosman2 (2945)
|
4/7/2011 12:46:04 AM
|
|
On 2011-04-06 16:48:41 -0400, Andreas Leitgeb said:
> 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.
>
> I do not intend to delve into the details of static, private
> or final methods, or final'ity of the class itself (and maybe
> others) avoiding these problems, but instead I'm curious, why
> Java just doesn't simply forbid the dangerous calls.
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.
-o
|
|
0
|
|
|
|
Reply
|
angrybaldguy (338)
|
4/7/2011 2:23:32 AM
|
|
Tom Anderson <twic@urchin.earth.li> wrote:
> On Wed, 6 Apr 2011, Andreas Leitgeb wrote:
>> 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?
> public abstract class Library {
> private List<Document> documents;
> protected Library() {
> documents = new ArrayList<Document>();
> Collection<String> titles = listDocuments();
> for (String title: titles) {
> Document doc = loadDocument(title);
> // do other preparatory stuff with the document
> documents.add(doc);
> }
> }
> protected abstract Collection<String> listDocuments();
> protected abstract Document loadDocument(String title);
> }
>
> public class FilesystemLibrary extends Library {
> // ...
> }
> [...]
Sorry, in my eyes, this is one of the anti-patterns.
> What are the alternatives?
It's hard to speculate about alternatives for an artificial example.
I'd say, that they exist for any reasonable specification of the problem,
and "back it up"(*) by declaring specifications for which no (or only
mindboggingly contrived) alternatives exist as unreasonable ;-)
(*): In German, we use double-quotes also to indicate tongue-in-cheek
formulations that aren't to be taken entirely literally/serious.
I've recently learned that they are not always thusly understood,
elsewhere. Therefore this explanation. Is there any common markup
for it that would be recognized in the English-speaking world?
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 8:36:49 AM
|
|
Lew <noone@lewscanon.com> wrote:
> Andreas Leitgeb wrote:
>>> 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?
>
> import org.apache.log4j.Logger;
> import static org.apache.log4j.getLogger;
> public class Foo
> {
> private final Logger logger = getLogger( getClass() );
> // ...
> }
I thought, I made it clear, that calling static methods was not relevant
to my question. So, kind of thanks for pointing out that I should have
written non-static in the very line of the question, rather than consider
that clear from the other (snipped by you) paragraphs of my post...
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 8:44:38 AM
|
|
Owen Jacobson <angrybaldguy@gmail.com> wrote:
> On 2011-04-06 16:48:41 -0400, Andreas Leitgeb said:
>
>> 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.
>>
>> I do not intend to delve into the details of static, private
>> or final methods, or final'ity of the class itself (and maybe
>> others) avoiding these problems, but instead I'm curious, why
>> Java just doesn't simply forbid the dangerous calls.
>
> It's hard to prove that a constructor never calls a virtual method. Consider:
> [ example of c'tor calling a private method "internalInit", which in turn
> calls virtual "virtualMethod". ]
Good point. The compiler couldn't (at least not statically) prevent
indirect calling of overridable non-static methods.
But otoh., it refuses to compile this:
String bad = (String) new Integer(42);
while allowing this: (sure bomb at runtime)
String bad = (String) (Object) new Integer(42);
So, not being able to prevent something happening indirectly,
doesn't imply that the direct way would need to be allowed, too.
So, it boils down to the original question of whether there is also
a good use of constructors (directly or indirectly) invoking
overridable virtual methods.
What it "good"? I have no exact definition, but if one of the
Java gurus (Brian Goetz, Joshua Bloch, James Gosling,...) ever
before suggested a pattern that would involve it, then chances
are good, that I'd accept it. Also, if the JSL or one of the big
Java-based projects used it. Tom's example (Library) didn't
convince me so far.
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 9:13:56 AM
|
|
On 11-04-07 05:36 AM, Andreas Leitgeb wrote:
[ SNIP ]
> (*): In German, we use double-quotes also to indicate tongue-in-cheek
> formulations that aren't to be taken entirely literally/serious.
> I've recently learned that they are not always thusly understood,
> elsewhere. Therefore this explanation. Is there any common markup
> for it that would be recognized in the English-speaking world?
It would be quotation marks, usually double quotes, just as in German.
Even Wikipedia (in its entry for Quotation Marks) refers to the use of
quotation not only for actual quotes, but also to denote irony or
unusual usage. Quotes are also used to indicate non-literal or
self-coined meanings, or to emphasize use of the word rather than its
meaning.
Having said that, there are varying levels of literacy in the
English-speaking world. Not everyone will be aware of the other usages
for quotation marks.
AHS
--
That's not the recollection that I recall...All this information is
certainly in the hands of the auditor and we certainly await his report
to indicate what he deems has occurred.
-- Halifax, Nova Scotia mayor Peter Kelly, who is currently deeply in
the shit
|
|
0
|
|
|
|
Reply
|
asandstrom3minus1 (421)
|
4/7/2011 10:16:22 AM
|
|
On 06 Apr 2011 20:48:41 GMT, Andreas Leitgeb
<avl@gamma.logic.tuwien.ac.at> wrote, quoted or indirectly quoted
someone who said :
>Is there any *good* use of having the constructor call a method
>that actually *can* be overridden in a subclass?
I have accidentally nailed myself by overriding a method used in a
constructor. I have yet to find a legitimate use for it. It is too
bad the compiler does not complain about it.
--
Roedy Green Canadian Mind Products
http://mindprod.com
Doing what the user expects with respect to navigation is absurdly important for user satisfaction.
~ anonymous Google Android developer
|
|
0
|
|
|
|
Reply
|
see_website (4858)
|
4/7/2011 10:20:18 AM
|
|
On Apr 7, 9:36=A0am, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:
....
> I'd say, that they exist for any reasonable specification of the problem,
> and "back it up"(*) by declaring specifications for which no (or only
> mindboggingly contrived) alternatives exist as unreasonable ;-)
>
> (*): In German, we use double-quotes also to indicate tongue-in-cheek
> formulations that aren't to be taken entirely literally/serious.
> I've recently learned that they are not always thusly understood,
> elsewhere. =A0Therefore this explanation. Is there any common markup
> for it that would be recognized in the English-speaking world?
I think we use the same convention in British English. I'm not certain
about our "colonial cousins".
The convention also spills over into spoken English, often using non-
verbal hints. There was also a fad at one time for people to raise
their hands to either side of their head and wriggle two fingers to
imitate quotes. I believe it's only used ironically now.
|
|
0
|
|
|
|
Reply
|
paul.cager (63)
|
4/7/2011 10:20:57 AM
|
|
On 2011-04-07, Andreas Leitgeb <avl@gamma.logic.tuwien.ac.at> wrote:
> Owen Jacobson <angrybaldguy@gmail.com> wrote:
>> On 2011-04-06 16:48:41 -0400, Andreas Leitgeb said:
>>
>>> 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.
>>>
>>> I do not intend to delve into the details of static, private
>>> or final methods, or final'ity of the class itself (and maybe
>>> others) avoiding these problems, but instead I'm curious, why
>>> Java just doesn't simply forbid the dangerous calls.
>>
>> It's hard to prove that a constructor never calls a virtual method. Consider:
>> [ example of c'tor calling a private method "internalInit", which in turn
>> calls virtual "virtualMethod". ]
>
> Good point. The compiler couldn't (at least not statically) prevent
> indirect calling of overridable non-static methods.
>
> But otoh., it refuses to compile this:
> String bad = (String) new Integer(42);
> while allowing this: (sure bomb at runtime)
> String bad = (String) (Object) new Integer(42);
>
> So, not being able to prevent something happening indirectly,
> doesn't imply that the direct way would need to be allowed, too.
>
> So, it boils down to the original question of whether there is also
> a good use of constructors (directly or indirectly) invoking
> overridable virtual methods.
>
> What it "good"? I have no exact definition, but if one of the
> Java gurus (Brian Goetz, Joshua Bloch, James Gosling,...) ever
> before suggested a pattern that would involve it, then chances
> are good, that I'd accept it. Also, if the JSL or one of the big
> Java-based projects used it. Tom's example (Library) didn't
> convince me so far.
>
Why should it complain about your second example? You tell the Compiler
explicitly "Please consider this Integer as Object and this Object as String,
I know types don't match but I know what I'm doing" (I'm programming in C at
the moment where the Compiler doesn't complain about things javac wouldn't
even compile as in your first example, so YMMV)
|
|
0
|
|
|
|
Reply
|
tobiasblass (33)
|
4/7/2011 10:58:31 AM
|
|
On 7 Apr., 10:44, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:
> Lew <no...@lewscanon.com> wrote:
> > Andreas Leitgeb wrote:
> >>> 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?
>
> > import org.apache.log4j.Logger;
> > import static org.apache.log4j.getLogger;
> > public class Foo
> > {
> > =A0 =A0 private final Logger logger =3D getLogger( getClass() );
> > =A0 // ...
> > }
>
> I thought, I made it clear, that calling static methods was not relevant
> to my question. =A0So, kind of thanks for pointing out that I should have
> written non-static in the very line of the question, rather than consider
> that clear from the other (snipped by you) paragraphs of my post...
Well, that is clear, but: getClass() isn't a static method. Note that
Lew did not post the usual idiom with log4j and the like which
typically looks something like this:
private static final Logger logger =3D Logger.getLogger(Foo.class);
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 =3D>
inefficient code.
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. Consider
class Foo {
private int v;
public Foo() {
v =3D init(); // allowed, because it's private
}
public void reinitialize() {
v =3D init();
}
private int init() {
// allowed, but only if init() is not invoked
// from a constructor:
int x =3D doSomething();
return x * 10 + 2;
}
/** Sub classes may override this.
* @return a number
*/
protected int doSomething() {
return 5;
}
}
Kind regards
robert
|
|
0
|
|
|
|
Reply
|
shortcutter (5767)
|
4/7/2011 11:14:36 AM
|
|
On 04/07/2011 04:44 AM, Andreas Leitgeb wrote:
> Lew<noone@lewscanon.com> wrote:
>> Andreas Leitgeb wrote:
>>>> 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?
>>
>> import org.apache.log4j.Logger;
>> import static org.apache.log4j.getLogger;
>> public class Foo
>> {
>> private final Logger logger = getLogger( getClass() );
>> // ...
>> }
>
> I thought, I made it clear, that calling static methods was not relevant
> to my question. So, kind of thanks for pointing out that I should have
> written non-static in the very line of the question, rather than consider
> that clear from the other (snipped by you) paragraphs of my post...
I did not realize that 'getClass()' was static.
--
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
|
|
0
|
|
|
|
Reply
|
noone7 (3512)
|
4/7/2011 11:19:08 AM
|
|
Arved Sandstrom <asandstrom3minus1@eastlink.ca> wrote:
> On 11-04-07 05:36 AM, Andreas Leitgeb wrote:
>> (*): In German, we use double-quotes also to indicate tongue-in-cheek
>> formulations that aren't to be taken entirely literally/serious.
>> I've recently learned that they are not always thusly understood,
>> elsewhere. Therefore this explanation. Is there any common markup
>> for it that would be recognized in the English-speaking world?
>
> It would be quotation marks, usually double quotes, just as in German.
> Even Wikipedia (in its entry for Quotation Marks) refers to the use of
> quotation not only for actual quotes, but also to denote irony or
> unusual usage. Quotes are also used to indicate non-literal or
> self-coined meanings, or to emphasize use of the word rather than its
> meaning.
Thanks for the info!
> Having said that, there are varying levels of literacy in the
> English-speaking world. Not everyone will be aware of the other
> usages for quotation marks.
:/
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 11:26:27 AM
|
|
Roedy Green <see_website@mindprod.com.invalid> wrote:
> On 06 Apr 2011 20:48:41 GMT, Andreas Leitgeb
><avl@gamma.logic.tuwien.ac.at> wrote, quoted or indirectly quoted
> someone who said :
>
>> Is there any *good* use of having the constructor call a method
>> that actually *can* be overridden in a subclass?
>
> I have accidentally nailed myself by overriding a method used in a
> constructor. I have yet to find a legitimate use for it. It is too
> bad the compiler does not complain about it.
I haven't (yet) been bitten by it, myself.
I just recently came to think about it.
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 11:32:44 AM
|
|
Paul Cager <paul.cager@googlemail.com> wrote:
>> (*): In German, we use double-quotes also to indicate tongue-in-cheek
>> formulations that aren't to be taken entirely literally/serious.
> I think we use the same convention in British English. I'm not certain
> about our "colonial cousins".
:-)
> The convention also spills over into spoken English, often using non-
> verbal hints. There was also a fad at one time for people to raise
> their hands to either side of their head and wriggle two fingers to
> imitate quotes. I believe it's only used ironically now.
Is that a consequence of the "Austin Powers" movies, or did the
movies only make fun of an already common behavioural pattern?
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 11:36:16 AM
|
|
Tobias Blass <tobiasblass@gmx.net> wrote:
>>> It's hard to prove that a constructor never calls a virtual method. Consider:
>>> [ example of c'tor calling a private method "internalInit", which in turn
>>> calls virtual "virtualMethod". ]
>> Good point. The compiler couldn't (at least not statically) prevent
>> indirect calling of overridable non-static methods.
>>
>> But otoh., it refuses to compile this:
>> String bad = (String) new Integer(42);
>> while allowing this: (sure bomb at runtime)
>> String bad = (String) (Object) new Integer(42);
> Why should it complain about your second example? You tell the Compiler
> explicitly "Please consider this Integer as Object and this Object as String,
It wasn't my intention to criticize that double-casts are allowed,
nor that single casts of incompatible types aren't. Just, that there
already is an example of something bad, that is indirectly possible
but directly forbidden.
> I know types don't match but I know what I'm doing"
Interestingly, those cases where such a double cast would
solve a real problem (namely casting between two interfaces
or between a non-final class and an interface & vice versa)
do already work with a single cast, so I don't really understand,
what double casts are really good for.
> (I'm programming in C at the moment where the Compiler doesn't
> complain about things javac wouldn't even compile as in your
> first example, so YMMV)
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'm doing C++, and recently I noticed a mistake of mine on
rereading it: I had tried to use polymorphism with objects
stored directly in an stl vector<baseclass>... ;-)
Changed it to vector<const baseclass*>, a few "."s to "->"s, and
added a few "new"s, allowing me to continue using polymorphism.
(The vector isn't meant to ever shrink till end of process, so no
extra "delete"s. Also, it only grows during initialization.)
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 12:19:29 PM
|
|
Lew <noone@lewscanon.com> wrote:
> On 04/07/2011 04:44 AM, Andreas Leitgeb wrote:
>> Lew<noone@lewscanon.com> wrote:
>>> Andreas Leitgeb wrote:
>>>>> 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?
>>> private final Logger logger = getLogger( getClass() );
>> I thought, I made it clear, that calling static methods was not relevant
>> to my question. So, kind of thanks for pointing out that I should have
>> written non-static in the very line of the question, rather than consider
>> that clear from the other (snipped by you) paragraphs of my post...
>
> I did not realize that 'getClass()' was static.
I did not realize that getClass() was overidable - a property I even
explicitly repeated in my question. For this, I assumed you were
talking of getLogger().
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 12:24:28 PM
|
|
Robert Klemme <shortcutter@googlemail.com> wrote:
> On 7 Apr., 10:44, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
> wrote:
>> Lew <no...@lewscanon.com> wrote:
>> > Andreas Leitgeb wrote:
>> >>> 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?
>>
>> > import org.apache.log4j.Logger;
>> > import static org.apache.log4j.getLogger;
>> > public class Foo
>> > {
>> > private final Logger logger = getLogger( getClass() );
>> > // ...
>> > }
>>
>> I thought, I made it clear, that calling static methods was not relevant
>> to my question. So, kind of thanks for pointing out that I should have
>> written non-static in the very line of the question, rather than consider
>> that clear from the other (snipped by you) paragraphs of my post...
>
> Well, that is clear, but: getClass() isn't a static method.
Because getClass() is final, it didn't occur to me, that he
would have put up *that* as an 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.
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 12:32:05 PM
|
|
On 7 Apr., 14:32, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:
> Robert Klemme <shortcut...@googlemail.com> wrote:
> > On 7 Apr., 10:44, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
> > wrote:
> >> Lew <no...@lewscanon.com> wrote:
> >> > Andreas Leitgeb wrote:
> >> >>> Is there any *good* use of having the constructor call a method th=
at
> >> >>> 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?
>
> >> > import org.apache.log4j.Logger;
> >> > import static org.apache.log4j.getLogger;
> >> > public class Foo
> >> > {
> >> > =A0 =A0 private final Logger logger =3D getLogger( getClass() );
> >> > =A0 // ...
> >> > }
>
> >> I thought, I made it clear, that calling static methods was not releva=
nt
> >> to my question. =A0So, kind of thanks for pointing out that I should h=
ave
> >> written non-static in the very line of the question, rather than consi=
der
> >> that clear from the other (snipped by you) paragraphs of my post...
>
> > Well, that is clear, but: getClass() isn't a static method.
>
> 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). =A0I reckon, language designers figured that allowing it
> > is more worthwhile than preventing it. =A0As Thomas pointed out you
> > would have to provide constructor arguments so subclasses can pass on
> > data they have created. =A0This 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). =A0You
> > would end up creating objects which then need to be discarded if the
> > super class constructor decides that he does not need them =3D>
> > 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
|
|
0
|
|
|
|
Reply
|
shortcutter (5767)
|
4/7/2011 12:55:05 PM
|
|
On Apr 7, 8:32=A0am, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:
> Robert Klemme <shortcut...@googlemail.com> wrote:
> > On 7 Apr., 10:44, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
> > wrote:
> >> Lew <no...@lewscanon.com> wrote:
> >> > Andreas Leitgeb wrote:
> >> >>> Is there any *good* use of having the constructor call a method th=
at
> >> >>> 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?
>
> >> > import org.apache.log4j.Logger;
> >> > import static org.apache.log4j.getLogger;
> >> > public class Foo
> >> > {
> >> > =A0 =A0 private final Logger logger =3D getLogger( getClass() );
> >> > =A0 // ...
> >> > }
>
> >> I thought, I made it clear, that calling static methods was not releva=
nt
> >> to my question. =A0So, kind of thanks for pointing out that I should h=
ave
> >> written non-static in the very line of the question, rather than consi=
der
> >> that clear from the other (snipped by you) paragraphs of my post...
>
> > Well, that is clear, but: getClass() isn't a static method.
>
> 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 =3D Foo.class.getSimpleName();
public String getFoot()
{
return FOOT;
}
private final String represent =3D "Insert "+ getFoot();
public String toString()
{
return represent;
}
}
class Bar extends Foo
{
private static final String BART =3D Bar.class.getSimpleName();
public String getFoot()
{
return BART;
}
}
--
Lew
|
|
0
|
|
|
|
Reply
|
lew (2143)
|
4/7/2011 3:10:46 PM
|
|
Owen Jacobson <angrybaldguy@gmail.com> writes:
> On 2011-04-06 16:48:41 -0400, Andreas Leitgeb said:
>
>> 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.
>>
>> I do not intend to delve into the details of static, private
>> or final methods, or final'ity of the class itself (and maybe
>> others) avoiding these problems, but instead I'm curious, why
>> Java just doesn't simply forbid the dangerous calls.
>
> 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.
--
Jim Janney
|
|
0
|
|
|
|
Reply
|
jjanney (252)
|
4/7/2011 4:26:42 PM
|
|
On 7 Apr, 14:19, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
wrote:
> 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.
|
|
0
|
|
|
|
Reply
|
alessiostalla (364)
|
4/7/2011 5:40:51 PM
|
|
On 2011-04-07, Alessio Stalla <alessiostalla@gmail.com> wrote:
> On 7 Apr, 14:19, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
> wrote:
>> 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.
Is there any case where the C compiler rejects casts? I cannot imagine an
example.
|
|
0
|
|
|
|
Reply
|
tobiasblass (33)
|
4/7/2011 7:00:46 PM
|
|
Tobias Blass <tobiasblass@gmx.net> wrote:
> On 2011-04-07, Alessio Stalla <alessiostalla@gmail.com> wrote:
>> On 7 Apr, 14:19, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at> wrote:
>>> 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.
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)
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 8:11:38 PM
|
|
Jim Janney <jjanney@shell.xmission.com> wrote:
> 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.
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 8:16:38 PM
|
|
Lew <lew@lewscanon.com> wrote:
>> >> > 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. ;-)
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 8:31:26 PM
|
|
Tobias Blass <tobiasblass@gmx.net> writes:
> On 2011-04-07, Alessio Stalla <alessiostalla@gmail.com> wrote:
>> On 7 Apr, 14:19, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
>> wrote:
>>> 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.
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 */
--
Jim Janney
|
|
0
|
|
|
|
Reply
|
jjanney (252)
|
4/7/2011 8:36:23 PM
|
|
Janney wrote:
> In Java evaluating new Bar() will throw an exception. =A0But 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. =A0After 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. =A0But 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?
--
Lew
|
|
0
|
|
|
|
Reply
|
lew (2143)
|
4/7/2011 9:05:01 PM
|
|
Andreas Leitgeb wrote:
> 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.
--
Lew
|
|
0
|
|
|
|
Reply
|
lew (2143)
|
4/7/2011 9:10:49 PM
|
|
On Thu, 7 Apr 2011, Jim Janney wrote:
> Owen Jacobson <angrybaldguy@gmail.com> writes:
>
>> 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();
>> }
>> }
>
> 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
--
Many of us adopted the File's slang as our own, feeling that we'd found a
tangible sign of the community of minds we'd half-guessed to be out there.
|
|
0
|
|
|
|
Reply
|
twic (2083)
|
4/7/2011 10:05:24 PM
|
|
This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.
--232016332-653822250-1302214136=:25031
Content-Type: TEXT/PLAIN; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 8BIT
On Thu, 7 Apr 2011, Lew wrote:
> On Apr 7, 8:32�am, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at>
> wrote:
>
>> 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.
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
--
Many of us adopted the File's slang as our own, feeling that we'd found a
tangible sign of the community of minds we'd half-guessed to be out there.
--232016332-653822250-1302214136=:25031--
|
|
0
|
|
|
|
Reply
|
twic (2083)
|
4/7/2011 10:08:56 PM
|
|
On Thu, 7 Apr 2011, Andreas Leitgeb wrote:
> Robert Klemme <shortcutter@googlemail.com> wrote:
>
>> 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.
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
--
Many of us adopted the File's slang as our own, feeling that we'd found a
tangible sign of the community of minds we'd half-guessed to be out there.
|
|
0
|
|
|
|
Reply
|
twic (2083)
|
4/7/2011 10:10:19 PM
|
|
Lew <lew@lewscanon.com> wrote:
> Andreas Leitgeb wrote:
>> 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.
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
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 10:15:03 PM
|
|
Lew <lew@lewscanon.com> writes:
> Janney wrote:
>> 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?
In the message I was replying to, in the text that you deleted, Owen
Jacobsen correctly observed that
>>> It's hard to prove that a constructor never calls a virtual method. Consider:
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".
--
Jim Janney
|
|
0
|
|
|
|
Reply
|
jjanney (252)
|
4/7/2011 10:15:23 PM
|
|
Tom Anderson <twic@urchin.earth.li> wrote:
>> 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,
I'd appreciate.
> but i thought a timely answer was desirable!
appreciated in hindsight :)
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/7/2011 10:24:55 PM
|
|
Tom Anderson <twic@urchin.earth.li> writes:
>> 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.
It's surprised me more than once. You'd think after the first time I
would remember, but no...
>
>> 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.
C++ is an interesting language. The interesting languages aren't always
the ones you want to do actual work in.
--
Jim Janney
|
|
0
|
|
|
|
Reply
|
jjanney (252)
|
4/7/2011 11:38:23 PM
|
|
On 06/04/2011 22:48, Andreas Leitgeb allegedly wrote:
> 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.
--
DF.
An escaped convict once said to me:
"Alcatraz is the place to be"
|
|
0
|
|
|
|
Reply
|
da.futt.news (225)
|
4/7/2011 11:51:29 PM
|
|
On 08/04/2011 01:51, Daniele Futtorovic allegedly wrote:
> 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.
|
|
0
|
|
|
|
Reply
|
da.futt.news (225)
|
4/7/2011 11:56:38 PM
|
|
On 4/7/2011 3:00 PM, Tobias Blass wrote:
> On 2011-04-07, Alessio Stalla<alessiostalla@gmail.com> wrote:
>> On 7 Apr, 14:19, Andreas Leitgeb<a...@gamma.logic.tuwien.ac.at>
>> wrote:
>>> 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.
> Is there any case where the C compiler rejects casts? I cannot imagine an
> example.
(struct tm)42;
--
Eric Sosman
esosman@ieee-dot-org.invalid
|
|
0
|
|
|
|
Reply
|
esosman2 (2945)
|
4/8/2011 12:59:36 AM
|
|
Andreas Leitgeb wrote:
> Lew wrote:
>> 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.
Something used by 'getClass()' is overridden somewhere inside. Otherwise its
behavior would not be what it is. Therefore there's an overridable method
being called somewhere in the example I gave. Therefore it's a valid example.
Give it a rest.
First you wanted to excoriate the example for being 'static', which it wasn't.
Then you wanted to excoriate it because the outer method is 'final', which
is irrelevant. All along you had an example in front of you where it was
useful to call an overridable method from a constructor, which is all that you
asked for. You didn't (originally) ask that the method be called through a
non-'final' method call. Instead you argued about points that had nothing to
do with the example's validity or its utility.
When you did add the rule that the call be directly to a non-'final' method, I
promptly showed the equivalent example adhering to that new rule.
What new rule are you going to add now, Mr. No True Scotsman?
--
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
|
|
0
|
|
|
|
Reply
|
noone7 (3512)
|
4/8/2011 1:22:37 AM
|
|
Tom Anderson wrote:
>> 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.
Fair enough. Good points.
--
Lew
Honi soit qui mal y pense.
http://upload.wikimedia.org/wikipedia/commons/c/cf/Friz.jpg
|
|
0
|
|
|
|
Reply
|
noone7 (3512)
|
4/8/2011 1:24:05 AM
|
|
Jim Janney wrote:
> Lew writes:
> 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".
Dude, I was just asking for explanation of something I admitted I did not
understand, and gave you what I admitted I understood to be a misimpression
already. I didn't quote everything you said because I naturally assumed you
knew what you'd said and would understand if I have just enough context to
establish my question. I guess not.
Sah-ree!
--
Lew
Buh-bye, now.
|
|
0
|
|
|
|
Reply
|
noone7 (3512)
|
4/8/2011 1:28:00 AM
|
|
On 2011-04-07 18:15:23 -0400, Jim Janney said:
> Lew <lew@lewscanon.com> writes:
>
>> Janney wrote:
>>> 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?
Lew, bog off.
> In the message I was replying to, in the text that you deleted, Owen
> Jacobsen correctly observed that
Jacobson. Like the UML guy, to my eternal mortification. :)
>>>> It's hard to prove that a constructor never calls a virtual method. Consider:
>
> 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.
Sure. C++'s objection initialization proceeds from the top down, just
like Java's, with the difference that the class of '*this' changes as
constructors complete. In your extension of my example, with
public class Foo { /* ... */ }
public class Bar extends Foo { /* ... */ }
this would mean that during Foo's constructor, 'this' points to a Foo
object, while during Bar's constructor, it points to a Bar object*.
However, in Java, constructor chaining is syntactically a statement, so
it appears that control flow begins in the most-derived class's
constructor before chaining upwards through each superclass's
constructor to Object() -- and that's exactly what happens under the
hood!
$ cat Surprise.java
class Surprise {
public Surprise(String s) {
super(); // explicit for the sake of discussion only.
}
}
class Subprise extends Surprise {
public Subprise() {
super("Hello, world!");
}
}
$ javap -classpath . Subprise -c
Compiled from "Surprise.java"
class Subprise extends Surprise{
public Subprise();
Code:
0: aload_0
1: ldc #1; //String Hello, world!
3: invokespecial #2; //Method Surprise."<init>":(Ljava/lang/String;)V
6: return
}
Given that JVM-level implementation of constructors, having the class
of 'this' change after the third instruction would be very tricky to
implement. C++ doesn't have this problem, since (a) constructor
chaining is NOT syntactically like a statement and (b) constructor
chaining doesn't have to compile like one, either.
Could it have been designed differently? Sure. Java's constructor
semantics are a weird-but-mostly-intuitive mix of C++'s constructors
and Smalltalk's initializers-are-just-methods approach (where you're
not forced to chain to a parent class's initializer at all). It's a
compromise, and like all compromises, it's not quite like any of the
alternatives; however, I think having the type of 'this' remain stable
is a useful feature. :)
-o
* And also that if Foo.Foo() captures 'this' in a field, Bar.Bar() can
compare this to the field using == and get true back, even though the
class has changed. 'new' is only allowed to introduce one distinct new
pointer.
|
|
0
|
|
|
|
Reply
|
angrybaldguy (338)
|
4/8/2011 3:21:39 AM
|
|
On 2011-04-07 23:21:39 -0400, Owen Jacobson said:
> C++'s objection initialization
What a revealing typo.
-o
|
|
0
|
|
|
|
Reply
|
angrybaldguy (338)
|
4/8/2011 3:39:25 AM
|
|
Daniele Futtorovic <da.futt.news@laposte-dot-net.invalid> wrote:
> On 08/04/2011 01:51, Daniele Futtorovic allegedly wrote:
>> class BaseClass implements [...] {
In principle this is the same kind of example as Tom's.
It has already been established by now, that this calling
of virtual methods from the c'tor *can* be used with caution
such that it doesn't necessarily lead to bugs.
Also, that alternatives exist, even though they may appear
to be less convenient at times.
What's left open and would interest me is, if this pattern is
already used in any of the big Java-based projects. E.g. a publically
accessible javadoc of the API of a lib documenting some protected
method to be "called from some superclass constructor and thus
the implementation should not assume 'this' instance to be
initialized to any degree."
Not that this existence or non-existence would have any real
impact on Java's future, of course...
|
|
0
|
|
|
|
Reply
|
avl1 (2656)
|
4/8/2011 9:30:28 AM
|
|
On 08/04/2011 11:30, Andreas Leitgeb allegedly wrote:
> Daniele Futtorovic<da.futt.news@laposte-dot-net.invalid> wrote:
>> On 08/04/2011 01:51, Daniele Futtorovic allegedly wrote:
>>> class BaseClass implements [...] {
>
> In principle this is the same kind of example as Tom's.
No. In Tom's example, the subclass provided functionality to the
superclass. In my example, the subclass adds to what the superclass does.
I also presented that example to illustrate how implementing the
Externalizable contract can impact the issue. Because Externalizable
mandates a no-arg c'tor, some mechanisms one might have used in similar
cases cannot be used there.
--
DF.
An escaped convict once said to me:
"Alcatraz is the place to be"
|
|
0
|
|
|
|
Reply
|
da.futt.news (225)
|
4/8/2011 6:02:28 PM
|
|
On Thu, 7 Apr 2011, Owen Jacobson wrote:
> Could it have been designed differently? Sure. Java's constructor
> semantics are a weird-but-mostly-intuitive mix of C++'s constructors and
> Smalltalk's initializers-are-just-methods approach (where you're not
> forced to chain to a parent class's initializer at all).
Since we're talking other languages, i'll mention Python, where in a
typical chained constructor:
class Subprise(Surprise):
def __init__(self):
super(Subprise, self).__init__()
not only is the call to the superclass __init__ method a perfectly normal
call, so is the use of super()!
tom
--
the logical extension of a zero-infinity nightmare topology
|
|
0
|
|
|
|
Reply
|
twic (2083)
|
4/8/2011 7:32:05 PM
|
|
"Tom Anderson" <twic@urchin.earth.li> wrote in message
news:alpine.DEB.2.00.1104072305290.25031@urchin.earth.li...
>
> 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.
That's how I've always thought about it, particularly since in 4GLs I've
helped implement, that's exactly what the analogous method did.
|
|
0
|
|
|
|
Reply
|
mscottschilling (1976)
|
4/10/2011 5:06:19 AM
|
|
|
49 Replies
32 Views
(page loaded in 0.286 seconds)
|