Created
March 16, 2020 13:33
-
-
Save martijndwars/a356368586b8304c9acc6e044e440909 to your computer and use it in GitHub Desktop.
Generics: why does this work?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.ArrayList; | |
import java.util.Arrays; | |
public class Main { | |
public static void main(String[] args) { | |
ArrayConstructor<Integer> inner = new ArrayConstructor<Integer>(); | |
inner.add("Foo"); | |
inner.add("Bar"); | |
inner.add("Baz"); | |
inner.add(42); | |
System.out.println(Arrays.toString(inner.materialize())); | |
System.out.println("Done"); | |
} | |
public static class ArrayConstructor<T> { | |
public ArrayList<T> instance; | |
public ArrayConstructor() { | |
instance = new ArrayList<T>(); | |
} | |
public void add(Object value) { | |
instance.add((T) value); | |
} | |
public T[] materialize() { | |
return (T[]) instance.toArray(); | |
} | |
} | |
} |
import java.util.ArrayList;
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
ArrayConstructor<Integer> inner = new ArrayConstructor<Integer>();
inner.add("Foo");
inner.add("Bar");
inner.add("Baz");
inner.add(42);
System.out.println(Arrays.toString(inner.materialize()));
System.out.println("Done");
System.out.println(inner.get(0));
Integer i = inner.get(0);
System.out.println(i);
}
public static class ArrayConstructor<T> {
public ArrayList<T> instance;
public ArrayConstructor() {
instance = new ArrayList<T>();
}
public T get(int index) {
return instance.get(index);
}
public void add(Object value) {
instance.add((T) value);
}
public T[] materialize() {
return (T[]) instance.toArray();
}
}
}
this compiles with:
$ javac Main.java
Note: Main.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
but running gives:
$ java Main
[Foo, Bar, Baz, 42]
Done
Foo
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
at Main.main(Main.java:15)
note how "Foo" still prints, although it would have wrong type here as well.
in the bytecode:
// System.out.println(inner.get(0)); (no cast after "get")
60: iconst_0
61: invokevirtual #14 // Method Main$ArrayConstructor.get:(I)Ljava/lang/Object;
64: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
// Integer i = inner.get(0); (notice the "checkcast" after get
67: aload_1
68: iconst_0
69: invokevirtual #14 // Method Main$ArrayConstructor.get:(I)Ljava/lang/Object;
72: checkcast #16 // class java/lang/Integer
75: astore_2
// System.out.println(i);
76: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
79: aload_2
80: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
compiling with -Xlint:unchecked
gives you this btw:
Main.java:31: warning: [unchecked] unchecked cast
instance.add((T) value);
^
required: T
found: Object
where T is a type-variable:
T extends Object declared in class ArrayConstructor
Main.java:35: warning: [unchecked] unchecked cast
return (T[]) instance.toArray();
^
required: T[]
found: Object[]
where T is a type-variable:
T extends Object declared in class ArrayConstructor
2 warnings
ah one last thing: the bytecode for some methods in ArrayConstructor
:
// aweld: no cast here at all
public void add(java.lang.Object);
Code:
0: aload_0
1: getfield #4 // Field instance:Ljava/util/ArrayList;
4: aload_1
5: invokevirtual #6 // Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
8: pop
9: return
// aweld: here we just have a checkcast for "object array"
public T[] materialize();
Code:
0: aload_0
1: getfield #4 // Field instance:Ljava/util/ArrayList;
4: invokevirtual #7 // Method java/util/ArrayList.toArray:()[Ljava/lang/Object;
7: checkcast #8 // class "[Ljava/lang/Object;"
10: areturn
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Why is there no type error anywhere? Why does
inner.materialize()
have the runtime typeObject[]
?