From: markspace on 14 Jun 2010 18:37 Lew wrote: > Simon Brooke <stillyet+n...(a)googlemail.com> wrote: >> OK, this is because selector-method resolution is done at compile time, >> not at run time as it would be in CLOS and in some other object oriented >> languages... yes? >> > > I don't know anything about CLOS. It's not relevant anyway, nor are > those other languages. Basically, Simon is correct. Java and C++ implement single dispatch in their method overloading resolution. Both languages have to implement double dispatch manually, which is the reason for the Visitor pattern. Lisp and a few other languages implement double dispatch (or multi-dispatch) natively, although I believe there's a considerable runtime cost overhead. C.f. <http://en.wikipedia.org/wiki/Double_dispatch>
From: Joshua Cranmer on 14 Jun 2010 20:02 On 06/14/2010 12:20 PM, Vikram wrote: > And we invoke the method as : test(null) : from another class, the > method with the String parameter is executed. Any particular reason > for this? JLS 3 � 15.12.2, your goto resource for pretty much any question about why a method invocation is doing what it is doing. A brief overview of what happens in this particular case: First, the compiler selects all the possible methods that the invocation could match based on name and arity (in the absence of varargs, which is a bit trickier) (� 15.12.1 and � 15.12.2.1 goes into this in more detail if you really want to know, but it's pretty intuitive). In your case, this is both of the test methods. After that, the compiler looks at types to figure stuff out. If you regress to pre-1.5 functionality, the subtyping is pretty simple. The compiler is basically asking "how well do each of these methods match what the user is trying?"; all expressions have types (the assignment of types is fairly straightforward, see most of � 15 if you really want to know in full gory detail), so it's trying to match the types to the types that the method supports--identifying matching methods by subtyping. All types have supertypes: it is pretty much the same relation between classes and superclasses, so all reference types are subtypes of Object. The null type's supertypes are all reference types other than null. In your case, you have an expression whose type is the null type. Since null is a subtype of String and Object, this phase of the search selects both test methods. In the next phase, we need to choose between them. The powers that be decided that the most intuitive thing to do is to select the "most specific" method, where "most specific" has particular meaning. In the absence of generics, if method A is more specific than method B, than the arguments of A have declared types that are subtypes of method B's arguments. Under this definition, test(String) is more specific than test(Object), so it is the one chosen. Note that if you also had a test(Set) method, there would be no most specific method, and the compiler would complain at you as a result. -- Beware of bugs in the above code; I have only proved it correct, not tried it. -- Donald E. Knuth
From: Arne Vajhøj on 14 Jun 2010 21:28 On 14-06-2010 16:11, Lew wrote: > Lew writes: >>> The parent 'equals()' method typically is one of those "other methods >>> with different semantics". The semantics of 'equals(Object)' will >>> differ from that of 'equals(Foo)' without special care, i.e., an >>> override of the former that invokes the latter. >>> >>> 'equals()' is typically called with an 'Object' argument, and >>> therefore the overload will not be invoked. Things like collections >>> depend on the method to have an override. No override yields >>> undesired behavior. >> > > ilan wrote: >> Hmm.. So it should rather be? >> >> public class Foo >> { >> ... > > Use @Override here If a recent Java version. > >> public boolean equals( Object other ) >> { >> ... >> } >> } >> > > The answer to your question depends on whether you want an override or > an overload. In the case of 'equals()' you want an override, or both. I think it would be rare when both would be wanted. Arne
From: Arne Vajhøj on 14 Jun 2010 21:26 On 14-06-2010 14:46, ilan wrote: > Lew<lew(a)lewscanon.com> writes: >> Lew wrote: >>>> Your method overload question is quite important, actually. It's a >>>> common mistake to overload 'equals()' when the intent is to override, >>>> e.g., >>>> >>>> public class Foo >>>> { >>>> ... >>>> public boolean equals( Foo other ) >>>> { >>>> ... >>>> } >>>> } >>>> >>>> This method will appear to work if passed a 'Foo' argument because the >>>> 'Foo' argument matches more specifically than the 'Object' argument to >>>> the other 'equals()' method in the class, but it will fail in general >>>> when clients pass an 'Object' argument and bypass the specific overload, >>>> getting the parent-class version instead. >>> >> >> Simon Brooke wrote: >>> Sorry, why is this a mistake? If a 'Bar' argument is passed, equals must >>> return false anyway. Does it matter which implementation of equals is >>> invoked? >>> >> >> Yes. As I already stated in my post, it will fail in the general case >> when clients invoke it with an 'Object' argument. You will then get >> the non-overridden 'equals(Object)' method from the parent class, >> which typically provides object identity comparison rather than value >> comparison, or else value comparison on the wrong fields. >> >>> I can see how this might matter for other methods with different >>> semantics, but why equals, particularly? >>> >> >> The parent 'equals()' method typically is one of those "other methods >> with different semantics". The semantics of 'equals(Object)' will >> differ from that of 'equals(Foo)' without special care, i.e., an >> override of the former that invokes the latter. >> >> 'equals()' is typically called with an 'Object' argument, and >> therefore the overload will not be invoked. Things like collections >> depend on the method to have an override. No override yields >> undesired behavior. >> > > Hmm.. So it should rather be? > > public class Foo > { > ... > public boolean equals( Object other ) > { > ... > } > } > > Thanks! I just learnt something. Yes. Arne
From: Arne Vajhøj on 14 Jun 2010 21:39
On 14-06-2010 13:46, Simon Brooke wrote: > On Mon, 14 Jun 2010 10:05:08 -0700, Lew wrote: >> Your method overload question is quite important, actually. It's a >> common mistake to overload 'equals()' when the intent is to override, >> e.g., >> >> public class Foo >> { >> ... >> public boolean equals( Foo other ) >> { >> ... >> } >> } >> >> This method will appear to work if passed a 'Foo' argument because the >> 'Foo' argument matches more specifically than the 'Object' argument to >> the other 'equals()' method in the class, but it will fail in general >> when clients pass an 'Object' argument and bypass the specific overload, >> getting the parent-class version instead. > > Sorry, why is this a mistake? If a 'Bar' argument is passed, equals must > return false anyway. Does it matter which implementation of equals is > invoked? > > I can see how this might matter for other methods with different > semantics, but why equals, particularly? equals(Object) is more polymorh than equals(Foo) ! Try and run this little demo: public class Equals { public static void main(String[] args) { Foo foo1 = new Foo(123); Foo foo2 = new Foo(123); System.out.println(foo1.equals(foo2)); Object ofoo1 = new Foo(123); Object ofoo2 = new Foo(123); System.out.println(ofoo1.equals(ofoo2)); Bar bar1 = new Bar(123); Bar bar2 = new Bar(123); System.out.println(bar1.equals(bar2)); Object obar1 = new Bar(123); Object obar2 = new Bar(123); System.out.println(obar1.equals(obar2)); } } class Foo { private int v; public Foo(int v) { this.v = v; } public boolean equals(Foo o) { return (v == o.v); } public int hashCode() { return v; } } class Bar { private int v; public Bar(int v) { this.v = v; } @Override public boolean equals(Object o) { return (o instanceof Bar) && (v == ((Bar)o).v); } @Override public int hashCode() { return v; } } The Bar class works as expected with polymorphism. The Foo class gives inconsistent results depending on the type of the reference. Arne |