www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Method hiding

reply bearophile <bearophileHUGS lycos.com> writes:
While answering to Larry Luther in a long thread in D.learn:
http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=19913

I have found an interesting difference that I didn't know between Java and D.
Here I have reduced the test cases:


// Java code
class A {
    void foo(A a) {}
}
class B extends A {
    void foo(B b) {}

    public static void main(String[] args) {
        A a = new A();
        B b = new B();
        b.foo(a);
    }
}



// D2 code
class A {
    void foo(A a) {}
}
class B : A {
    void foo(B b) {}
}
void main() {
    A a = new A;
    B b = new B;
    b.foo(a);// comment this out and use warnings
}


If you comment out the last line in the D2 code, and you use warnings, DMD
tells you at compile time:

test.d(5): Error: class test.B test.A.foo(A a) is hidden by B


If you uncomment that line DMD shows:

test.d(12): Error: function test.B.foo (B b) is not callable using argument
types (A)
test.d(12): Error: cannot implicitly convert expression (a) of type test.A to
test.B


While the Java code compiles and runs with no errors.
It seems in D the B.foo() hides A.foo() even if there is no overriding at all
here.

The presence of that warning tells me this is not an implementation bug, D is
designed this way on purpose. But what is the rationale behind this difference
(that at best will puzzle Java programmers trying to use D)?

Bye,
bearophile
May 27 2010
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Thu, 27 May 2010 17:00:08 -0400, bearophile <bearophileHUGS lycos.com>  
wrote:

 While answering to Larry Luther in a long thread in D.learn:
 http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D.learn&article_id=19913

 I have found an interesting difference that I didn't know between Java  
 and D. Here I have reduced the test cases:


 // Java code
 class A {
     void foo(A a) {}
 }
 class B extends A {
     void foo(B b) {}

     public static void main(String[] args) {
         A a = new A();
         B b = new B();
         b.foo(a);
     }
 }



 // D2 code
 class A {
     void foo(A a) {}
 }
 class B : A {
     void foo(B b) {}
 }
 void main() {
     A a = new A;
     B b = new B;
     b.foo(a);// comment this out and use warnings
 }


 If you comment out the last line in the D2 code, and you use warnings,  
 DMD tells you at compile time:

 test.d(5): Error: class test.B test.A.foo(A a) is hidden by B


 If you uncomment that line DMD shows:

 test.d(12): Error: function test.B.foo (B b) is not callable using  
 argument types (A)
 test.d(12): Error: cannot implicitly convert expression (a) of type  
 test.A to test.B


 While the Java code compiles and runs with no errors.
 It seems in D the B.foo() hides A.foo() even if there is no overriding  
 at all here.

 The presence of that warning tells me this is not an implementation bug,  
 D is designed this way on purpose. But what is the rationale behind this  
 difference (that at best will puzzle Java programmers trying to use D)?

This is actually the first question I posted on this newsgroup in 2007. It's called method "hijacking", look for it on the D website for a thorough explanation. Note that this is actually the default behavior in C++ (I didn't know until I asked the question and tried it, it's pretty obscure). But the behavior is overridable, you can do this: class B : A { alias A.foo foo; void foo(A a) {} } Which means "also look at A for resolving foo." However, doing this may lead to further issues. I think if you had a class C that derived from B, calling B.foo(c) would result in an ambiguity without a cast. -Steve
May 27 2010
next sibling parent bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:

 This is actually the first question I posted on this newsgroup in 2007.

I am very late then, sorry for not asking this is D.learn.
 It's called method "hijacking", look for it on the D website for a  
 thorough explanation.  Note that this is actually the default behavior in  
 C++ (I didn't know until I asked the question and tried it, it's pretty  
 obscure).

I have read this, about in the middle: http://www.digitalmars.com/d/2.0/hijack.html Thank you for the explanations and thanks to Walter for that page. I vaguely remember reading that page, but I did remember it only about free functions. I appreciate D design here :-)
 But the behavior is overridable, you can do this:
 
 class B : A {
    alias A.foo foo;
    void foo(A a) {}
 }
 
 Which means "also look at A for resolving foo."

OK.
 However, doing this may lead to further issues.  I think if you had a  
 class C that derived from B, calling B.foo(c) would result in an ambiguity  
 without a cast.

I will experiment some about this. Thank you and bye, bearophile
May 27 2010
prev sibling parent bearophile <bearophileHUGS lycos.com> writes:
Steven Schveighoffer:

 However, doing this may lead to further issues.  I think if you had a  
 class C that derived from B, calling B.foo(c) would result in an ambiguity  
 without a cast.

This is D code: import std.c.stdio: puts; class A { void foo(A a) { puts("A.foo"); } } class B : A { alias A.foo foo; void foo(B b) { puts("B.foo"); } } class C : B {} void main() { A a = new A(); B b = new B(); C c = new C(); b.foo(a); // Prints: A.foo b.foo(c); // Prints: B.foo } -------------------- A Java translation: class A { void foo(A a) { System.out.println("A.foo"); } } class B extends A { void foo(B b) { System.out.println("B.foo"); } } class C extends B { public static void main(String[] args) { A a = new A(); B b = new B(); C c = new C(); b.foo(a); // Prints: A.foo b.foo(c); // Prints: B.foo } } This time they both call the same methods, b.foo(c) calls B.foo(B), probably because C is closer to B than A in the hierarchy :-) In normal programs I'd like to avoid writing this code. Bye, bearophile
May 27 2010