Note that there are some explanatory texts on larger screens.

plurals
  1. POJava, Static Method Binding and Generics all rolled up with some Method Overloading
    primarykey
    data
    text
    <p>So as the title implies my question is a bit odd and complicated. I know what I'm about to do breaks all the rules of "good" programming practices but hey, what's life if we don't live a little?</p> <p>So what I did was create the following program. (Not this was part of a larger experiment to really try and understand generics so some of the function names maybe a bit out of order)</p> <pre><code>import java.util.*; public class GenericTestsClean { public static void test2() { BigCage&lt;Animal&gt; animalCage=new BigCage&lt;Animal&gt;(); BigCage&lt;Dog&gt; dogCage=new BigCage&lt;Dog&gt;(); dogCage.add(new Dog()); animalCage.add(new Cat()); animalCage.add(new Dog()); animalCage.printList(dogCage); animalCage.printList(animalCage); } public static void main(String [] args) { //What will this print System.out.println("\nTest 2"); test2(); } } class BigCage&lt;T&gt; extends Cage&lt;T&gt; { public static &lt;U extends Dog&gt; void printList(List&lt;U&gt; list) { System.out.println("*************"+list.getClass().toString()); for(Object obj : list) System.out.println("BigCage: "+obj.getClass().toString()); } } class Cage&lt;T&gt; extends ArrayList&lt;T&gt; { public static void printList(List&lt;?&gt; list) { System.out.println("*************"+list.getClass().toString()); for(Object obj : list) System.out.println("Cage: "+obj.getClass().toString()); } } class Animal { } class Dog extends Animal { } class Cat extends Animal { } </code></pre> <p>Now what is confusing me is that this compiles fine with <strong>javac 1.6.0_26</strong> but when I run it I get the following class cast exception:</p> <pre><code>Test 2 *************class BigCage BigCage: class Dog *************class BigCage Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to Dog at BigCage.printList(GenericTestsClean.java:31) at GenericTestsClean.test2(GenericTestsClean.java:13) at GenericTestsClean.main(GenericTestsClean.java:21) </code></pre> <p>A number of things to note here:</p> <ol> <li>The two printList are <strong>NOT</strong> overriding but overloading each other as expected(They have different types because the generic types of their arguments are different). This can be verified by using the @Override annotation</li> <li>Changing the <code>void printList(List&lt;?&gt;)</code> method in <strong>class Cage</strong> to be non-static generates an appropriate compile time error</li> <li>Changing the method <code>void &lt;U extends Dog&gt; printList(List&lt;U&gt;)</code> in <strong>class BigCage</strong> to <code>void &lt;U&gt; printList(List&lt;U&gt;)</code> generates an appropriate error.</li> <li>In <strong>main()</strong> calling <strong>printList()</strong> through the <strong>class BigCage</strong> (ie BigCage.printList(...)) generates the same runtime error</li> <li>In <strong>main()</strong> calling <strong>printList()</strong> through the <strong>class Cage</strong> (ie Cage.printList(...)) works as expected only calling the version of <strong>printList</strong> in <strong>Cage</strong></li> <li>If I copy the definition for <code>printList(List&lt;?&gt;)</code> to <strong>class BigCage</strong> from <strong>class Cage</strong>, which will hide the definition in <strong>class Cage</strong>, I get the appropriate compiler error</li> </ol> <p>Now if I had to take a shot in the dark as to what is going on here, I'd say the compiler is screwing up because it's working in multiple phases: <em>Type Checking</em> and <em>Overloaded Method Resolution</em>. During the type checking phase we get through the offending line because <strong>class BigCage</strong> inherited <code>void printList(List&lt;?&gt;)</code> from <code>class Cage</code> which will match any old List we throw at it, so sure we have a method that will work. However once it comes time to resolve with method to actually call we have a problem due to Type Erasure which causes both <code>BigCage.printList</code> and <code>Cage.printList</code> to have the exact same signature. This means when compiler is looking for a match for <code>animalCage.printList(animalCage);</code> it will choose the first method it matches (and if we assume it starts at the bottom with BigCage and works its why up to Object) it'll find <code>void &lt;U extends Dog&gt; printList(List&lt;U&gt;)</code> first instead of the correct match <code>void printList(List&lt;?&gt;)</code></p> <p><strong>Now for my real question</strong>: How close to the truth am I here? Is this a known bug? Is this a bug at all? I know how to get around this problem, this is more of an academic question.</p> <blockquote> <p><strong>**EDIT**</strong></p> <p>As few people have posted below, this code will work in Eclipse. My specific question deals with javac version 1.6.0_26. Also, I'm not sure if I completely agree with Eclipse in this case, even though it works, because adding a <code>printList(List&lt;?&gt;)</code> to <strong>BigCage</strong> will result in a compile time error in Eclipse and I can't see reason why it should work when the same method is inherited verses manually added (See <strong>Note 6</strong> above).</p> </blockquote>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload