Java Generics and Variable Arguments



  • The following code

    public class Test {
            public static void main(String[] args) {
                    IBase s = new Sub();
                    s.remove("String");
            }
    }
     
    interface IBase<K extends Comparable<? super K>> {
     
            public void remove(K ... keys);
    }
    class Base<K extends Comparable<? super K>> implements IBase<K> {
            public void remove(K... keys) {
                    System.out.println("removed keys in Base");
            }
    }
     
    interface ISub extends IBase<String> {
    }
    class Sub extends Base <String> implements ISub {
            public void remove(String... keys) {
                    System.out.println("removed keys in Sub");
            }

    throws a ClassCastException

    <FONT color=#ff0000 size=1>

    Exception in thread "main" </FONT><FONT color=#000080 size=1>java.lang.ClassCastException</FONT><FONT color=#ff0000 size=1>: [Ljava.lang.Comparable;

    at Sub.remove(</FONT><FONT color=#000080 size=1>Test.java:1</FONT><FONT color=#ff0000 size=1>)

    at Test.main(</FONT><FONT color=#000080 size=1>Test.java:5</FONT><FONT color=#ff0000 size=1>)

    </FONT>

    I can't paramatize the IBase s =... as the original code a series of IBase Objects are held in a List<IBase<?>>.

    Does anyone have any ideas?



  • Compiling with Sun javac with the -Xlint:unchecked option (if you use another compiler or an IDE, check the documentation for the equivalent) gives the following message:

    Test.java:4: warning: [unchecked] unchecked call to remove(K...) as a member of the raw type IBase
    s.remove("String");
    ^
    1 warning
    suggesting that line 3 needs to be
                    IBase<String> s = new Sub();

    - and indeed, with that change it runs correctly.  What happens is that main calls the remove method with an array of type Comparable[], then Sub.remove tries to cast it to a String[], which fails.

     (EDIT: sorry, I didn't notice the

    @Gir said:

    I can't paramatize the IBase s =... as the original code a series of IBase Objects are held in a List<IBase<?>>.

    part.  In that case, I don't know what you should do, although it might help to see some more detailed code.)

    You can find out exactly what a class file is doing with the javap tool included in the JDK (the Sun JDK, at least, dunno about others), which can disassemble the bytecode.  Of course, you have to be able to be able to read JVM assembly code - http://java.sun.com/developer/TechTips/2000/tt0829.html and http://www.ibm.com/developerworks/ibm/library/it-haggar_bytecode/ might be helpful, as well as the almighty Google.  In this case, the relevant parts are

    public static void main(java.lang.String[]);
    Code:
    0: new #2; //class Sub
    3: dup
    4: invokespecial #3; //Method Sub."<init>":()V
    7: astore_1
    8: aload_1
    9: iconst_1
    10: anewarray #4; //class java/lang/Comparable
    13: dup
    14: iconst_0
    15: ldc #5; //String String
    17: aastore
    18: invokeinterface #6, 2; //InterfaceMethod IBase.remove:([Ljava/lang/Comparable;)V
    23: return

    from Test and

    public void remove(java.lang.Comparable[]);
    Code:
    0: aload_0
    1: aload_1
    2: checkcast #5; //class "[Ljava/lang/String;"
    5: invokevirtual #6; //Method remove:([Ljava/lang/String;)V
    8: return

    from Sub ([Ljava/lang/String; means String[]).



  • There really isn't any way to get around this. Either the List needs to be given to you as List<IBase<String>>, or IBase needs to have a method like getType() added for finding its type at runtime. It's somewhat similar to how certain methods of EnumSet and EnumMap accept a Class argument.

    It may help to think of it from a conceptual point of view, rather than a compiling point of view. How can you be expected to remove strings from an IBase that may or may not even deal with strings? (Actually, if the method is really a remove method, you might be able to change its argument type to Object. Collection.remove is written that way, since it's legal to try to remove things that were not in the collection to begin with.)



  • Hai from robert...java doubt

    Why Vector is Synchronized..And  Why ArrayList is not Synchronized.....

     

    please explain me...

     

    William 



  • Kind sir, please to email me the codez



  •  me to.

     

    also can you kind sirs tell me when java generics will be as good as .net and not suffer from this insanity? 



  • @Jonathan Holland said:

     me to.

     

    also can you kind sirs tell me when java generics will be as good as .net and not suffer from this insanity? 

    For the Microsoft kiddiez definition of "good," probably never. When you're a more advanced programmer, you'll understand why the original poster's issue is a good thing. It's preventing him from writing code that will break. Of course, we all know Microsoft prefers to let people write, and even encourages people to write, crappy, broken code. Windows drivers alone are proof of that.


Log in to reply