which foo(x)

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Rick Trotter

    which foo(x)

    I was trying to write some polymorphic application code and found that
    a superclass method implementation gets invoked when I expect a
    subclass implementation. Here's how I have abstracted the problem.

    I have a base class - ClassA. I have a subclass of ClassA - ClassB.
    Both classes implement foo() - exact same method signature.

    My application code instantiates instances of ClassB (and siblings)
    via a marshaler, and only knows that the object it references is a
    "kind of" ClassA. I simulate this as shown below.

    ClassA a = (ClassA) Class.forName(" ClassB").newIns tance();

    Now, if I send a.foo(), I see that the implementation overriden in
    ClassB is invoked - as I had hoped and expected.


    But really what I am aiming for is something with a different twist.

    I have another class hierarchy with a base class - ClassX - and a
    subclass - ClassY.

    I have implemented ClassA.foo(Clas sX) and ClassB.foo(Clas sY).

    Instances of ClassX and ClassY are created outside the context of the
    code that invokes foo(...). All this code knows is that it's got a
    reference to an object which is a "kind of" ClassX. I simulate this as
    shown below.

    ClassX x = (ClassX) Class.forName(" ClassY").newIns tance();

    If, in this scenario, I send a.foo(x) I see that it is the super
    implementation of foo(...) that is invoked rather than the subclass
    implementation of foo(...).

    This was a big surprise to me. And it may seem as puzzling to some
    that I found it surprising. After all, you note, the Java compiler
    chose exactly the right implementation based on the information that I
    provided.

    I can kind of accept this, although I would argue that this behavior
    makes polymorphic code really fragile and difficult to debug, since
    runtime behavior is different from the language semantics due to ...
    ?compiler optimization?. (Coming to Java from a Smalltalk background,
    as I do, the complexity here seems a bit bizarre)

    But wait. By this reasoning, shouldn't I have gotten the superclass
    implementation of foo(void) in the first scenario?

    Just by way of exploring this puzzle a little further, I added another
    scenario. Suppose I have a variable declared as "ClassB b", and send
    "foo(x)" (where, as above 'x' is an instance of ClassY). This is at
    least consistent with scenario #2 - I get the super implementation
    because the compiler doesn't know that 'x' is actually a reference to
    an instance of ClassY.

    So far, then, it seems like scenario #1 is a bug, since - although
    it's what I wanted - it's inconsistent with the other two scenarios.
    Is there another explanation?

    -rht

    public class Testcase5 {

    public static void main(String argv[]) {
    ClassA a;
    ClassB b;
    ClassX x;
    try {
    a = (ClassA) Class.forName(" ClassB").newIns tance();
    b = (ClassB) Class.forName(" ClassB").newIns tance();
    x = (ClassX) Class.forName(" ClassY").newIns tance();
    } catch (Exception e) {
    System.err.prin tln(e.getMessag e());
    return;
    }

    b.foo(); // out: "B.foo()"
    a.foo(); // out: "B.foo()"

    b.foo(x); // out: "A.foo(x)"
    a.foo(x); // out: "A.foo(x)"
    }
    }
  • Raymond DeCampo

    #2
    Re: which foo(x)

    Rick Trotter wrote:[color=blue]
    > I was trying to write some polymorphic application code and found that
    > a superclass method implementation gets invoked when I expect a
    > subclass implementation. Here's how I have abstracted the problem.
    >
    > I have a base class - ClassA. I have a subclass of ClassA - ClassB.
    > Both classes implement foo() - exact same method signature.
    >
    > My application code instantiates instances of ClassB (and siblings)
    > via a marshaler, and only knows that the object it references is a
    > "kind of" ClassA. I simulate this as shown below.
    >
    > ClassA a = (ClassA) Class.forName(" ClassB").newIns tance();[/color]
    Reflection is not necessary here. This will do:

    ClassA a = new ClassB();
    [color=blue]
    >
    > Now, if I send a.foo(), I see that the implementation overriden in
    > ClassB is invoked - as I had hoped and expected.
    >
    >
    > But really what I am aiming for is something with a different twist.
    >
    > I have another class hierarchy with a base class - ClassX - and a
    > subclass - ClassY.
    >
    > I have implemented ClassA.foo(Clas sX) and ClassB.foo(Clas sY).
    >
    > Instances of ClassX and ClassY are created outside the context of the
    > code that invokes foo(...). All this code knows is that it's got a
    > reference to an object which is a "kind of" ClassX. I simulate this as
    > shown below.
    >
    > ClassX x = (ClassX) Class.forName(" ClassY").newIns tance();[/color]
    Again, this is clearer:

    ClassX x = new ClassY();[color=blue]
    >
    > If, in this scenario, I send a.foo(x) I see that it is the super
    > implementation of foo(...) that is invoked rather than the subclass
    > implementation of foo(...).
    >
    > This was a big surprise to me. And it may seem as puzzling to some
    > that I found it surprising. After all, you note, the Java compiler
    > chose exactly the right implementation based on the information that I
    > provided.
    >
    > I can kind of accept this, although I would argue that this behavior
    > makes polymorphic code really fragile and difficult to debug, since
    > runtime behavior is different from the language semantics due to ...
    > ?compiler optimization?. (Coming to Java from a Smalltalk background,
    > as I do, the complexity here seems a bit bizarre)[/color]

    The issue here is that method arguments do not resolve
    "polymorphicall y". That is, the polymorphism is based on method
    signatures and the target object only.

    So, in effect, when you thought you were overriding ClassA.foo(Clas sX)
    when you wrote ClassB.foo(Clas sY), you were really just overloading the
    method.

    This can be demonstrated without using ClassB. If you define
    ClassA.foo(Clas sY) and then do the same experiment you will get the same
    results. For example:

    ClassA a = new ClassA();
    ClassX x = new ClassY();
    ClassY y = new ClassY();
    a.foo(x); // invokes ClassA.foo(Clas sX)
    a.foo(y); // invokes ClassA.foo(Clas sY)

    The reason the methods are invoked as above is that the method signature
    is matched to the method call upon compile time. Whether or not the
    method invocation will ultimately be polymorphically determined is not
    relevant.
    [color=blue]
    >
    > But wait. By this reasoning, shouldn't I have gotten the superclass
    > implementation of foo(void) in the first scenario?
    >
    > Just by way of exploring this puzzle a little further, I added another
    > scenario. Suppose I have a variable declared as "ClassB b", and send
    > "foo(x)" (where, as above 'x' is an instance of ClassY). This is at
    > least consistent with scenario #2 - I get the super implementation
    > because the compiler doesn't know that 'x' is actually a reference to
    > an instance of ClassY.
    >
    > So far, then, it seems like scenario #1 is a bug, since - although
    > it's what I wanted - it's inconsistent with the other two scenarios.
    > Is there another explanation?[/color]

    Yes; you must remember that only the object the method is invoked on
    comes into play for polymorphism; the arguments do not change the method
    call polymorphically .

    This can be a little jarring the first time you encounter it, but you
    should be able to come to terms with it eventually :)

    HTH,
    Ray

    Comment

    Working...