Please explain this polymorphism twist to me.

M

Martin

Hi there,

I discovered a strange twist in polymorphism that leaves me in the
dark. If you can figure out what the following output will be, then
please explain to me why.

public class OverloadTest {

public class Visitor {
public void visit(Super s) {
System.out.println("Visitor.visit(Super)");
}

public void visit(Sub s) {
System.out.println("Visitor.visit(Sub)");
}
}

public class Super {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

public class Sub extends Super {
}

public static void main(String[] args) {
OverloadTest o = new OverloadTest();
Sub s = o.new Sub();
s.accept(o.new Visitor());
}
}


-------------- The answer is: (scroll down)
-
-
-
-
-
-
-
-
-
-
-
"Visitor.visit(Super)"
That means, the call "visitor.visit(this);" in the type Super is
handled by "Visitor.visit(Super)" instead of "Visitor.visit(Sub)".
This occurs as especially strange to me because a look in the debugger
confirms that the actual (dynamic) type of "this" is Sub! In my
opinion, the call should therefore be dynamically handled by
"Visitor.visit(Sub)".

I hope I didn't confuse you with Super-Sub, now, and you can tell me
where I need to review the JLS to turn on my light again.
 
O

Owen Jacobson

Hi there,

I discovered a strange twist in polymorphism that leaves me in the
dark. If you can figure out what the following output will be, then
please explain to me why.

public class OverloadTest {

    public class Visitor {
        public void visit(Super s) {
            System.out.println("Visitor.visit(Super)");
        }

        public void visit(Sub s) {
            System.out.println("Visitor.visit(Sub)");
        }
    }

    public class Super {
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }

    public class Sub extends Super {
    }

    public static void main(String[] args) {
        OverloadTest o = new OverloadTest();
        Sub s = o.new Sub();
        s.accept(o.new Visitor());
    }

}

-------------- The answer is: (scroll down)
-
-
-
-
-
-
-
-
-
-
-
"Visitor.visit(Super)"
That means, the call "visitor.visit(this);" in the type Super is
handled by "Visitor.visit(Super)" instead of "Visitor.visit(Sub)".
This occurs as especially strange to me because a look in the debugger
confirms that the actual (dynamic) type of "this" is Sub! In my
opinion, the call should therefore be dynamically handled by
"Visitor.visit(Sub)".

I hope I didn't confuse you with Super-Sub, now, and you can tell me
where I need to review the JLS to turn on my light again.

In

public class Super {
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

the static (compile-time) type of the expression 'this' is Super. You
will have to override accept in Sub (you can use identical code),
where the static type of 'this' is Sub.

Static types are *always* used to select methods to dispatch to, in
Java.

-o
 
T

Todd

Martin,

I fell into the same trap you did - I thought that
"Vistor.visit(Sub)" ought to have been the output.

After some thought and experiment, I believe the
answer lies in the 'this' reference in the 'Super'
class. 'this' is an instance of 'Super', not 'Sub'.
Overriding the accept method from with 'Sub' with
the same code as in 'Super' provides the expected
results.

What follows are my experimental methods.

Todd

public class Super {
public void accept(Visitor visitor) {
System.out.println( "Super test: " + (this instanceof
Super) );
visitor.visit(this);
}
}

public class Sub extends Super {
public void accept(Visitor visitor) {
System.out.println( "Sub test: " + (this instanceof Sub) );
visitor.visit(this);
}
}
 
A

Andreas Leitgeb

Martin said:
I discovered a strange twist in polymorphism that leaves me in the
dark. If you can figure out what the following output will be, then
please explain to me why.

There is only one object, whose runtime type will have an effect
on the choice of the method actually run: the object to the left of
the method-name. Everything else is treated as statically typed,
that means: the compiler sees the "this" pointer as being of type
"Super", in Super's accept, and therefore calls Visitor's visit(Super s).
I hope I didn't confuse you with Super-Sub, now, and you can tell me
where I need to review the JLS to turn on my light again.

In the chapter about overloading. Not in the chapter about
polymorphism, because your example didn't exercise that in a
way relevant to the result.

For further experimentation, change Sub such:

public class Sub extends Super {
public void accept(Visitor v) {
System.out.println("I'm a Sub!");
super.accept(v);
System.out.println("Hey, but I'm really a Sub!");
}
}

and change the main-method to do this:
OverloadTest o = new OverloadTest();
Super s = o.new Sub(); // change just the variable's type
s.accept(o.new Visitor());

The result will be, that still visitor(Super) is called
from Super.accept() (overloading: only static tye matters)
but that is being called from Sub.accept(), even though
the variable on which accept() was called was of type
Super (here polymorphism is used).

If you see any typoes, correct them. I didn't try it myself.
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Martin schreef:
| Hi there,
|
| I discovered a strange twist in polymorphism that leaves me in the
| dark. If you can figure out what the following output will be, then
| please explain to me why.

<snip>

| "Visitor.visit(Super)"
| That means, the call "visitor.visit(this);" in the type Super is
| handled by "Visitor.visit(Super)" instead of "Visitor.visit(Sub)".
| This occurs as especially strange to me because a look in the debugger
| confirms that the actual (dynamic) type of "this" is Sub! In my
| opinion, the call should therefore be dynamically handled by
| "Visitor.visit(Sub)".
|
| I hope I didn't confuse you with Super-Sub, now, and you can tell me
| where I need to review the JLS to turn on my light again.

Yes, I stepped into this trap once as well. You need to override
accept(Visitor) in each subclass. Others have explained well why this
is so.

The proper thing to do would be: do not use overloading, then you
wouldn’t have thought of this!

So: (note the names in Visitor)

public class OverloadTest {

~ public class Visitor {
public void visitSuper(Super s) {
System.out.println("Visitor.visit(Super)");
}

public void visitSub(Sub s) {
System.out.println("Visitor.visit(Sub)");
}
~ }

~ public class Super {
public void accept(Visitor visitor) {
visitor.visitSuper(this);
}
~ }

~ public class Sub extends Super {
public void accept(Visitor visitor) {
visitor.visitSub(this);
}
~ }

~ public static void main(String[] args) {
OverloadTest o = new OverloadTest();
Sub s = o.new Sub();
s.accept(o.new Visitor());
~ }
}

Overloading is not always considered a good thing in programming
languages, and this is one of the reasons why. One can do without easily.

HTH, H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
http://aouw.org
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.4-svn0 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iD8DBQFIEaFxe+7xMGD3itQRAjUSAJ9rNVJ6+qDiF4Q7eVs4NdSowtmxrACfe/F6
PUkHbjKf/KOI6FuFnOzP0zI=
=rcv1
-----END PGP SIGNATURE-----
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Lew schreef:
| Hendrik Maryns wrote:
|> -----BEGIN PGP SIGNED MESSAGE-----
|> Hash: SHA1
|>
|> Martin schreef:
|> | Hi there,
|> |
|> | I discovered a strange twist in polymorphism that leaves me in the
|> | dark. If you can figure out what the following output will be, then
|> | please explain to me why.
|>
|> <snip>
|>
|> | "Visitor.visit(Super)"
|> | That means, the call "visitor.visit(this);" in the type Super is
|> | handled by "Visitor.visit(Super)" instead of "Visitor.visit(Sub)".
|> | This occurs as especially strange to me because a look in the debugger
|> | confirms that the actual (dynamic) type of "this" is Sub! In my
|> | opinion, the call should therefore be dynamically handled by
|> | "Visitor.visit(Sub)".
|> |
|> | I hope I didn't confuse you with Super-Sub, now, and you can tell me
|> | where I need to review the JLS to turn on my light again.
|>
|> Yes, I stepped into this trap once as well. You need to override
|> accept(Visitor) in each subclass. Others have explained well why this
|> is so.
|>
|> The proper thing to do would be: do not use overloading, then you
|> wouldn’t have thought of this!
|>
|> So: (note the names in Visitor)
|>
|> public class OverloadTest {
|>
|> ~ public class Visitor {
|> public void visitSuper(Super s) {
|> System.out.println("Visitor.visit(Super)");
|> }
|>
|> public void visitSub(Sub s) {
|> System.out.println("Visitor.visit(Sub)");
|> }
|> ~ }
|>
|> ~ public class Super {
|> public void accept(Visitor visitor) {
|> visitor.visitSuper(this);
|> }
|> ~ }
|>
|> ~ public class Sub extends Super {
|> public void accept(Visitor visitor) {
|> visitor.visitSub(this);
|> }
|> ~ }
|>
|> ~ public static void main(String[] args) {
|> OverloadTest o = new OverloadTest();
|> Sub s = o.new Sub();
|> s.accept(o.new Visitor());
|> ~ }
|> }
|>
|> Overloading is not always considered a good thing in programming
|> languages, and this is one of the reasons why. One can do without
|> easily.
|
| But it isn't as bad as unnecessary coupling. Isn't the point of the
| Visitor pattern that the Visitor knows nothing about the classes that it
| visits? Now Sub and Super both depend on Visitor, and Visitor depends
| on both Sub and Super. That's a circular dependency, and it's a Very
| Bad Thing. There's nothing wrong with overloading methods; it's one of
| the seminal idioms of object-oriented programming and Java in particular.
|
| Good example of an antipattern to avoid, though.

Then how would you implement this, if I may ask?

Ok, there’s several flavors of visitors. I was thinking about a visitor
which knows what it is visiting, i.e. a visitor which does the
recursion/navigation itself. What’s the difference between visit(Sub
sub) and visitSub(Sub sub)? Nothing, except the name. And I don’t see
how you can do without.

Also, a Visitable will always accept a certain type of visitor, so it
will also know its interface. Then why not have it call the proper
method from the start.

I see the circular dependency, but I think it is inherent in the visitor
pattern.

H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
http://aouw.org
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.4-svn0 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iD8DBQFIEdtre+7xMGD3itQRAhhaAJ9/3rlBSpNZDiXf0K5v4cR0AZ6+rQCfSVgj
/8Uga/M9WE7XwqmZbT8w+yc=
=utvL
-----END PGP SIGNATURE-----
 
M

Martin

There is only one object, whose runtime type will have an effect
on the choice of the method actually run: the object to the left of
the method-name. Everything else is treated as statically typed,
that means: the compiler sees the "this" pointer as being of type
"Super", in Super's accept, and therefore calls Visitor's visit(Super s).

Ok, that's where my mistake was. I knew the target object (to the left
of the
method name) was resolved at run-time. However, I wasn't aware, that
parameters
are matched at compile time.
I hope I didn't confuse you with Super-Sub, now, and you can tell me
where I need to review the JLS to turn on my light again.

In the chapter about overloading. Not in the chapter about
polymorphism, because your example didn't exercise that in a
way relevant to the result.

For further experimentation, change Sub such:
[...]
The result will be, that still visitor(Super) is called
from Super.accept() (overloading: only static tye matters)
but that is being called from Sub.accept(), even though
the variable on which accept() was called was of type
Super (here polymorphism is used).

Thanks for the clarification.

To Owen Jacobsen:
I knew that overloading fixes the problem.
My problem is that in the application which I need to extend
the method Super.accept(Visitor) is actually final. Therefore,
I cannot override it in Sub. I am reluctant to change the
original implementation.

Thanks everybody for your input.
 
A

Andreas Leitgeb

Martin said:
I knew that overloading fixes the problem.
My problem is that in the application which I need to extend
the method Super.accept(Visitor) is actually final. Therefore,
I cannot override it in Sub. I am reluctant to change the
original implementation.

The object you have in Visitor.visit(Super s) is still a
Sub object, so you could try "instanceof Sub" to branch
to visit(Sub s)'s behaviour. Not a nice solution, but
not worse than the overloaded methods in the first place.

Or you can call (from visit) to some non-final method of
Super, which you override in Sub.
e.g. in visit(Super s): ... s.toString() ...
If your reluctance is only about un-final-ing accept(...),
then you can also add a new method that will do the class-
specific parts of visit.
 
A

Arne Vajhøj

Martin said:
Ok, that's where my mistake was. I knew the target object (to the left
of the
method name) was resolved at run-time. However, I wasn't aware, that
parameters
are matched at compile time.

It is a speed issue.

The method can be saved as an address of the code so no testing
is needed.

To check the argument at runtime on the other hand would be
expensive.

Arne
 
D

Daniel Pitts

Lew said:
I really don't understand the objection to method overloads. They taste
great and are less filling.
Because they lead to confusion and are often abused (such as this case).

What would be the harm in calling it visitSuper and visitSub? It makes
it clearer to the caller exactly what is happing. One of the few times
(I don't know for sure, but I might say the only time) that overloading
makes any kind of sense is with operator overloading, which isn't
possible in Java.

Now, if Java supported multiple dispatch, that might make method
overloading more useful. It also would have prevented the confusion of
the OP in this case, by doing what they had expected.
 
A

Andreas Leitgeb

I like overloading very much for completely distinct types
like e.g. one version for String, one for int, and perhaps
another one for String and int (as 2 args).

I do dislike them (just personal taste and experience) for
two types, one of which is a subtype of the other. It
requires more care to not fall for wrong expectations.

Daniel Pitts said:
What would be the harm in calling it visitSuper and visitSub?

In the case at hand (by the OP) that wouldn't have helped at all,
since there would have been no place where visitSub() would have
been called from (the access() was final in the base class).

If you need overloading for some base class and its derived
classes, it's much better to factor out the differing parts
of the code and place them into methods of these classes,
and use polymorphism.
 
A

Arne Vajhøj

Andreas said:
I like overloading very much for completely distinct types
like e.g. one version for String, one for int, and perhaps
another one for String and int (as 2 args).

I do dislike them (just personal taste and experience) for
two types, one of which is a subtype of the other. It
requires more care to not fall for wrong expectations.

I agree.

It is very bad practice to write code that require people
to sit and read the JLS when trying to understand the code.

Arne
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Andreas Leitgeb schreef:
|> Lew wrote:
|>> Andreas Leitgeb wrote:
|>>> The object you have in Visitor.visit(Super s) is still a Sub object,
|>>> so you could try "instanceof Sub" to branch
|>>> to visit(Sub s)'s behaviour. Not a nice solution, but
|>>> not worse than the overloaded methods in the first place.
|>> I really don't understand the objection to method overloads. They
taste
|>> great and are less filling.
|
| I like overloading very much for completely distinct types
| like e.g. one version for String, one for int, and perhaps
| another one for String and int (as 2 args).
|
| I do dislike them (just personal taste and experience) for
| two types, one of which is a subtype of the other. It
| requires more care to not fall for wrong expectations.
|
|> What would be the harm in calling it visitSuper and visitSub?
|
| In the case at hand (by the OP) that wouldn't have helped at all,
| since there would have been no place where visitSub() would have
| been called from (the access() was final in the base class).

To the contrary: he would (hopefully) have noticed that his visitSub()
method was never used, and maybe have solved the problem by himself.

| If you need overloading for some base class and its derived
| classes, it's much better to factor out the differing parts
| of the code and place them into methods of these classes,
| and use polymorphism.

But that is impossible when using the visitor pattern. And sometimes
you really don’t want to overburden your class hierarchy with methods
which only peripherally matter to it (like having a Builder go through a
Formula to make an Automaton: Formulas shouldn’t know anything about
automata). So although it tightly couples the Visitor and the Visited,
it can decouple the Visited from stuff which is done with it, but not
relevant to it (like Automaton in my example).

H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
http://aouw.org
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.4-svn0 (GNU/Linux)
Comment: Using GnuPG with SUSE - http://enigmail.mozdev.org

iD8DBQFIFZgRe+7xMGD3itQRAtpGAJ9gjKxgYzfHgPKOB/N96W7M4IgxawCfYcPy
1A7yvKoR/nxnEwudjkD1uA4=
=L1bK
-----END PGP SIGNATURE-----
 

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

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
DewittMill
Top