I have a simple generic method that creates a list of n elements and returns it:
import java.util.*;
class A {
public static <T> List<T> init(int n) {
List<T> l = new ArrayList<T>();
while (n --> 0) l.add(null);
return l;
}
public static void main(String[] args) {
List<String> x = init(5);
f(x);
}
public static void f(List<String> l) {
System.out.println("l: " + l);
}
}
It works as expected:
$ javac A.java && java A
l: [null, null, null, null, null]
But if I eliminate the extra variable:
import java.util.*;
class A {
public static <T> List<T> init(int n) {
List<T> l = new ArrayList<T>();
while (n --> 0) l.add(null);
return l;
}
public static void main(String[] args) {
f(init(5));
}
public static void f(List<String> l) {
System.out.println("l: " + l);
}
}
it no longer compiles
$ javac A.java && java A
A.java:9: f(java.util.List<java.lang.String>) in A cannot be applied to (java.util.List<java.lang.Object>)
f(init(5));
^
1 error
Why?
But yet this works:
import java.util.*;
class A {
public static <T> List<T> init(int n) {
List<T> l = new ArrayList<T>();
while (n --> 0) l.add(null);
return l;
}
public static <T> T id(T t) {
return t;
}
public static void main(String[] args) {
List<String> x = init(5);
f(id(x));
}
public static void f(List<String> l) {
System.out.println("l: " + l);
}
}
Why?
Java relies on inference to figure out what the type variables are in a situation where they are not explicitly defined.
In your first example:
List<String> x = init(5);
f(x);
The compiler infers that you are calling <String> init because x is a List<String>.
In your second example:
f(init(5));
The compiler cannot infer that you are calling <String> init because you are not explicitly telling it (via A. <String> init(5)) nor are you assigning it to an appropriate variable.
In your third example:
List<String> x = init(5);
f(id(x));
The compiler infers that you are calling <List<String>> id which returns a List<String> to f.
The compiler isn't too smart about generic inference. Unless you explicitly state what the type arguments are, either by using a variable or by directly passing them to the method, it will not be able to figure them out.
Here are the relevant sections of the JLS if you're curious about the specifics:
JLS §15.12.2.7
JLS §15.12.2.8
If you leave out the extra variable the compiler has no way to infer the type parameter T when calling init(5). It assumes T to be Object and hence the compiler error.
With the extra variable declared as a List<String> x the compiler infer T to be String.
First, the fix;
f(A.<String>init(5)); // compiles
Now, why: The original code compiled because java could infer the type due to being assigned to a typed variable. But inference doesn't work when being passed to a typed parameter.
The fix uses the syntax for explicitly specifying the type when calling a typed method.
f(init(5));
This calls f() with the argument directly received from init(). However, init() now returns a List of T. With T not specified Java just uses Object because it is the base class for all objects. List<Object> is not List<String>, so the method signature and the parameters don't match.
List<String> x = init(5);
f(x);
Here you are putting the List<Object> to a variable of the type List<String>. This converts it, because String is of course a subclass of Object. Also the conversion succeeds because null can also be converted to String as well as any other class. Then x's type matches the method signature.
List<String> x = init(5);
f(id(x));
This is basically the same thing. Now, because x has the type List<String>, id()'s type T is String. This way the return value of id() is also List<String>, which matches the signature.
In both the first and the third example, you stated that you are talking about a List<String>, yet on the second one you said f(init(5)), which could be a List<Integer>. I'm not 100% that's the only reason, but check it out :)
Related
The following code is working fine for m2() but is throwing a ClassCastException when I use m1().
The only difference between m1 and m2 is the number of arguments.
public class Test {
public static void m1() {
m3(m4("1"));
}
public static void m2() {
m3(m4("1"), m4("2"));
}
public static void m3(Object... str) {
for (Object o : str) {
System.out.println(o);
}
}
public static <T> T m4(Object s) {
return (T) s;
}
public static void main(String[] args) {
m1();
}
}
My question is - Does varargs not work with a single argument when we use generics?
PS : This is not related to ClassCastException using Generics and Varargs
Let's skip the fact that you ignored an unchecked cast warning for now and try to understand why this happened.
In this statement:
Test.m3(Test.m4("1"));
There is one inferred type, which is the return type of m4. If one is to use it outside the m3 invocation context, as in:
Test.m4("1"); // T is Object
T is inferred as Object. One can use a type witness to force the compiler to use a given type:
Test.<String>m4("1"); // T is String
...or by using the expression in an assignment context:
String resString = Test.m4("1"); // T is String
Integer resInt = Test.m4("1"); // T is Integer <-- see the problem?
... or in an invocation context:
Integer.parseInt(Test.m4("1")); // T is String
Long.toString(Test.m4("1")); // T is Long
Now, back to Test.m3(Test.m4("1"));: I couldn't find a reference for this, but I believe the compiler is forced to make T resolve to the parameter type of m3, which is Object[]. This means that T, which has to coincide with the parameter type of m3, is therefore resolved to Object[], and that makes it as though you specified generic types as:
Test.m3(Test.<Object[]>m4("1")); // this is what is happening
Now, because m4 is not returning an Object[], m3 is receiving a String, which leads to the inescapable ClassCastException.
How to solve it?
The first way to fix this is to specify a correct type argument for m4:
Test.m3(Test.<String>m4("1"));
With this, String is the return type of m4, and m3 is called with a single String object (for the Object... var-arg), as if you had written:
String temp = m4("1");
m3(temp);
The second approach was suggested in #Ravindra Ranwala's deleted answer. In my opinion, this boils down to heeding compiler warnings:
public static <T> T m4(Object s) {
return (T) s; // unchecked cast
}
The unchecked cast warning simply tells you that the compiler (and the runtime) are not going to enforce type compatibility, simply because T is not known where you cast. The following version is type-safe, but it also makes the compiler use String as the return type of m4 as well as the type of the parameter to m3:
public static <T> T m4(T s) {
return s;
}
With this, m3(m4("1")); still uses Object... as the parameter type of m3, while keeping String the return type of m4 (i.e., a string value is used as the first element of the Object array).
Because in the method implementation the array is only read and nothing is stored in the array. However, if a method would store something in the array it could attempt to store an alien object in the array, like putting a HashMap<Long,Long> into a HashMap<String,String>[]. Neither the compiler nor the runtime system could prevent it.
Here is another example that illustrates the potential danger of ignoring the warning issued regarding array construction in conjunction with variable argument lists.
static <T> T[] method_1(T t1, T t2) {
return method_2(t1, t2); // unchecked warning
}
static <T> T[] method_2( T... args) {
return args;
}
public static void main(String... args) {
String[] strings = method_1("bad", "karma"); // ClassCastException
}
warning: [unchecked] unchecked generic array creation of type T[] for
varargs parameter
return method_2(t1, t2);
As in the previous example, the array's component type is non-reifiable and due to type erasure the compiler does not create a T[] , but an Object[] instead. Here is what the compiler generates:
Example (same a above, after translation by type erasure):
public final class Test {
static Object[] method_1( Object t1, Object t2) {
return method_2( new Object[] {t1, t2} ); // unchecked warning
}
static Object[] method_2( Object[] args) {
return args;
}
public static void main(String[] args) {
String[] strings = (String[]) method_1("bad", "karma"); // ClassCastException
}
}
The unchecked warning is issued to alert you to the potential risk of
type safety violations and unexpected ClassCastExceptions
In the example, you would observe a ClassCastException in the main() method where two strings are passed to the first method. At runtime, the two strings are stuffed into an Object[]; note, not a String[] .
The second method accepts the Object[] as an argument, because after type erasure Object[] is its declared parameter type. Consequently, the second method returns an Object[] , not a String[] , which is passed along as the first method's return value. Eventually, the compiler-generated cast in the main() method fails, because the return value of the first method is an Object[] and no String[]
Conclusion
It is probably best to avoid providing objects of non-reifiable types where a variable argument list is expected. You will always receive an unchecked warning and unless you know exactly what the invoked method does you can never be sure that the invocation is type-safe.
You have to use a Class instance of T to cast since the generic type erasure during compilation
public class Test {
public static void m1() {
m3(m4("1", String.class));
}
public static void m2() {
m3(m4("1", String.class), m4("2", String.class));
}
public static void m3(final Object... str) {
for (Object o : str) {
System.out.println(o);
}
}
public static <T> T m4(final Object s, Class<T> clazz) {
return clazz.cast(s);
}
public static void main(String[] args) {
m1();
m2();
}
}
$java Test
1
1
2
Varargs and Generics don't mix to well in Java. This is because
Varags implemented by having an array of the respective type at runtime (array of Object in your case)
Arrays and Generics are just incompatible. You can't have an Array of String-Lists.
I have below generic method that returns a generic array:
public static <T> T[] genericMethod1(List<T> input) {
T[] res = (T[]) new Object[input.size()];
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
But later when I try to get the result array with:
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(1);
Integer[] i = (Integer[]) genericMethod1(list); // 1) Runtime error
Integer j = genericMethod2(list); // 2) works
For case 1, I always get error at runtime:
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;
Anybody can explain why and how to return the generic array properly? Thanks.
Below is my understanding, please correct me if I'm wrong.
As Tim mentioned, type erasure happened at compile time, so in bytecode, each T object is just type Object, meanwhile, compiler will add type cast from Object to T "properly".
Say T is an Integer, where T is declared, it's Object. For where it's referred, it's type cast (implicitly) to T.
EXCEPT that if T[] array is declared, it's Object[], and where the array is referred, it stays Object[]. No implicit cast to T[] happens.
The explanation for what you are seeing is due to something called type erasure. Here is what your genericMethod() will look like after the compiler performs type erasure:
public static Object[] genericMethod(List input) {
Object[] res = new Object[input.size()];
int i = 0;
for (Object t : input) {
res[i] = t;
i++;
}
return res;
}
In other words, this method will return an array of type Object. There is no way to cast an Object[] to an Integer[] because they are not the same type. If you want your method to be able to dynamically return the type you want, then you can use Array.newInstance(). This will require also passing in the type of the array you want as an input parameter:
public static <T> T[] genericMethod(Class<T> clazz, List<T> input) {
#SuppressWarnings("unchecked")
T[] res = (T[]) Array.newInstance(clazz, input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
Now your code snippet will run without error:
LinkedList<Integer> list = new LinkedList<Integer>();
Integer[] i = genericMethod(Integer.class, list);
Update:
Your second method, genericMethod2(), will look like this after type erasure:
public static Object genericMethod2(List input) {
return input.get(0);
}
It will return the first element of the input list, cast to Object. Here is your usage of that method:
Integer j = genericMethod2(list);
The compiler will try to cast the output from genericMethod2() to Integer:
Integer j = (Integer)genericMethod2(list);
This cast is legal, because every Integer is also an Object, and furthermore it succeeds here because you passed in a collection of Integer. This second method is not the same scenario as the first one you highlighted for us.
When calling the method, genericMethod you are assuming that it returns array of integers, which is NOT correct. It actually returns array of type Object at runtime.
List<Integer> input = new ArrayList<Integer>();
input.add(1);
Object[] output = genericMethod(input);
for(Object obj : output){
System.out.println("Value= "+ (Integer)obj);
}
So we need to cast the individual content of the array.
One general guidline is that we shouldn't mix ARRAY and GENERICS in Java.
Update:
Reference from Effective Java:
In Summary, arrays and generics have very different type rules. Arrays are covariant and reified; generics are invariant and erased. As a consequcne, arrays provide runtime type safety but not compile-time type safety and vice versa for generics. Generally speaking, arrays and generics don’t mix well. If you find yourself mixing them and getting compile-time error or warnings, your first impulse should be to replace the arrays with lists.
Another way is to do it like in java.util.ArrayList.toArray(T[]).
You pass the type of Array to that Method, if it's big enough it will be reused, otherwise an new Array is generated.
Example:
List<Integer> intList = new ArrayList<>();
intList.add(Integer.valueOf(1));
intList.add(Integer.valueOf(2));
intList.add(Integer.valueOf(3));
Integer[] array = intList.toArray(new Integer[] {});
System.out.println(Arrays.toString(array));//Will Print [1, 2, 3]
Implementation of ArrayList.toArray(T[]) see here.
After Java 8 was released, you can leverage constructor references to return a generic array. While there are more straightforward options to convert List to Integer[], I am explaining the concept here with kinda minimal changes to your code:
I am not touching your genericMethod2() implementation
For genericMethod1, let's add a second parameter that will accept a constructor reference. Then you can call the apply() function on the constructor reference to create a generic array.
you pass Integer[]::new to this new parameter. Integer[]::new is treated as (int x) => new Integer[x]
fi.apply(input.size()) calls (int x) => new Integer[x] with argument input.size(). The result is a Integer[]. This is why the IntFunction parameter has generic type T[]
import java.util.List;
import java.util.function.IntFunction;
import java.util.LinkedList;
public class RetGenericArr {
public static <T> T[] genericMethod1(List<T> input, IntFunction<T[]> fi) {
T[] res = fi.apply(input.size());
int i = 0;
for (T t : input) {
res[i] = t;
i++;
}
return res;
}
public static <T> T genericMethod2(List<T> input) {
return input.get(0);
}
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<Integer>();
list.addFirst(1);
list.addFirst(2);
// umm I am uncomfortable about the variable naming below:
Integer[] i = genericMethod1(list, Integer[]::new); // Now no error/warning. Note that I am not casting now
for (int e: i) {
System.out.println(e);
}
Integer j = genericMethod2(list);
}
}
I have a sort of util method to transform a varargs of a type into an array of that type - it looks like this:
public K[] array(K... ks) {
return ks;
}
The use case is so that instead of defining an array when calling a method which requires an array, you can simply do array(val1, val2, val3).
However, IntelliJ gives me heap pollution warnings. I understand what this means to an extent, but I don't have much experience with the specifics - so, I would like to know whether I can add #SafeVarargs and whether this method is actually safe.
IntelliJ says:
Problem synopsis Possible heap pollution from parameterized
vararg type at line 249
Problem resolution Make final and annotate as #SafeVarargs
K is declared as the type parameter of a class, along with V.
No, it's not safe - if called from another method which is using generics. Here's a complete example which looks okay, but throws an exception:
class Utility<K> {
public K[] array(K... ks) {
return ks;
}
public K[] otherMethod(K k1, K k2) {
return array(k1, k2);
}
}
class Test {
public static void main(String[] args) throws Exception {
Utility<String> util = new Utility<String>();
// Bang!
String[] array = util.otherMethod("foo", "bar");
}
}
When the compiler creates the bytecode for otherMethod, it can't create an array of the right kind to pass into array, because it doesn't know the type of K. Due to type erasure, it just creates an Object[] with the values. So in main, there's a hidden cast from the result of otherMethod to String[]... and that fails at execution time.
If you call array directly from code which really knows the types of the arguments, then it's fine, because the implicitly-created array will be of the right type.
You could just tell your method how to convert to the appropriate array. One way I found was to pass the array into the method with the variables, and then copy to it.
public K[] otherMethod(K[] parent, K k1, K k2) {
List<K> list = new ArrayList<K>();
Collections.addAll(list, array(k1, k2));
list.toArray(parent);
return parent;
}
Now the output depends on the Collections.toArray() method, which returns null if there isn't enough space in the array, or if there is extra space the unused values will be null.
class Test {
public static void main(String[] args) throws Exception {
Utility<String> util = new Utility<String>();
String[] array = util.array("one", "two", "three");
array = util.otherMethod(array, "x", "y");
printArr(array); // prints: x y null
Utility<Integer> util2 = new Utility<Integer>();
Integer[] intarray = util2.otherMethod(new Integer[1], 1, 2);
printArr(intarray); // prints: null
Integer[] intarray = util2.otherMethod(new Integer[2], 1, 2);
printArr(intarray); // prints: 1 2
}
static void printArr(Object[] objArr) {
for (Object o:objArr) System.out.print(o+"\t");
}
}
This question already has answers here:
Java generics type erasure of method parameters
(3 answers)
Closed 9 years ago.
Consider Following code from Java Puzzlers
class Gloam<T>{
String glom(Collection<?> objs ) {
System.out.println("collection");
String result = "";
for (Object o : objs ){
result += o;
}
return result;
}
int glom(List <Integer> ints ) {
System.out.println("List");
int result = 0;
for ( int i : ints )
result += i ;
return result;
}
public static void main(String[] args) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam().glom(strings));
}
}
When I run this program it gives class cast exception, But if I provide any Generic argument for Gloam class in main method it works fine.
public static void main(String[] args) {
List<String> strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam<Date>().glom(strings));
}
I don't understand how generic works in class type parameter ?
With no generic type passed into constructor, all types are erased and the compiler is presented with this choices
String glom ( Collection );
int glom ( List );
The type is also erased from strings variable defined in main, so its type is List.
Because List is more specific than Collection it chooses int glom ( List ).
Now, if you have specified the generic parameter, then no type erasure happens, and compiler knows that it cannot match int glom ( List<Integer> ) to List<String>, so it falls back to String glom ( Collection<?> )
Once you fail to provide the generic type parameter, all generic typing for the whole class is gone in the eyes of the compiler. The class essentially becomes:
class Gloam<T> {
String glom(Collection objs) {
System.out.println("collection");
String result = "";
for (Object o : objs) {
result += o;
}
return result;
}
int glom(List ints) {
System.out.println("List");
int result = 0;
for (int i : ints)
result += i;
return result;
}
public static void main(String[] args) {
List strings = Arrays.asList("1", "2", "3");
System.out.println(new Gloam().glom(strings));
}
}
So now the compiler will choose the int glom(List ints) override since that's the most-specific override that matches the call. But it will also result in the class cast exception. When you supply the generic parameter, generics are retained and the String glom(Collection<?> objs ) override matches the call (which is passing a List<String>), whereas int glom(List <Integer> ints ) does not match because String is not Integer.
You can use generic to distinguish the method in Java. The JVM doesn't see this type however provided the argument or return type is different it will still compile in the Sun/Oracle compiler. This doesn't compile for the IBM/eclipse compiler.
This shows you want is happening at the byte code level.
Given some class SomeBaseClass, are these two method declarations equivalent?
public <T extends SomeBaseClass> void myMethod(Class<T> clz)
and
public void myMethod(Class<? extends SomeBaseClass> clz)
For the caller: yes, they are equivalent.
For the code inside the method: no.
The difference is that within the code of the first example you can use the type T (for example to hold an object created by clz.newInstance()), while in the second you can't.
No, they're not. With the first definition, you can use the type T inside the method definition, e.g. create an ArrayList<T> or return T. With the second definition, that's not possible.
Bounded wildcards are subject to certain restrictions to avoid heap pollution.
When you use the wildcard ? extends X you know you can read generic information, but you cannot write.
For instance
List<String> jedis = new ArrayList<String>();
jedis.add("Obiwan");
List<? extends CharSequence> ls = jedis
CharSequence obiwan = ls.get(0); //Ok
ls.add(new StringBuffer("Anakin")); //Not Ok
The compiler avoided heap pollution when you tried to add a CharSequence (i.e. StringBuffer) to the collection. Because the compiler cannot be sure (due to wildcards) that the actual implementation of the collection is of type StringBuffer.
When you use ? super X you know you can write generic information, but you cannot be sure of the type of what you read.
For instance
List<Object> jedis = new ArrayList<Object>();
jedis.add("Obiwan");
List<? super String> ls = jedis;
ls.add("Anakin"); //Ok
String obiwan = ls.get(0); //Not Ok, we can´t be sure list is of Strings.
In this case, due to wildcards, the compiler knows that the actual implementation of the collection could be anything in the ancestors of String. Thus it cannot guarantee that what you will get will be a String. Right?
This same restrictions are the ones you would be subject too in any declaration with bounded wildcards. These are typically known as the get/put principle.
By using a type parameter T you change the story, from the method standpoint you are not using a bounded wildcard but an actual type and therefore you could "get" and "put" things into instances of the class and the compiler would not complain.
For instance, consider the code in Collections.sort method. If we write a method as follows, we would get a compile error:
public static void sort(List<? extends Number> numbers){
Object[] a = numbers.toArray();
Arrays.sort(a);
ListIterator<? extends Number> i = numbers.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((Number)a[j]); //Not Ok, you cannot be sure the list is of Number
}
}
But if you write it like this, you can do the work
public static <T extends Number> void sort(List<T> numbers){
Object[] a = numbers.toArray();
Arrays.sort(a);
ListIterator<T> i = numbers.listIterator();
for (int j=0; j<a.length; j++) {
i.next();
i.set((T)a[j]);
}
}
And you could even invoke the method with collections bounded with wildcards thanks to a thing called capture conversion:
List<? extends Number> ints = new ArrayList<Integer>();
List<? extends Number> floats = new ArrayList<Float>();
sort(ints);
sort(floats);
This could not be achieved otherwise.
In summary, as others said from the caller standpoint they are alike, from the implementation standpoint, they are not.
No. On top of my head, I can think of the following differences:
The two versions are not override-equivalent. For instance,
class Foo {
public <T extends SomeBaseClass> void myMethod(Class<T> clz) { }
}
class Bar extends Foo {
public void myMethod(Class<? extends SomeBaseClass> clz) { }
}
does not compile:
Name clash: The method myMethod(Class) of type Bar has the same erasure as myMethod(Class) of type Foo but does not override it
If a type parameter appears more than once in a method signature, it always represents the same type, but if a wildcard appears more than once, each occurrence may refer to a different type. For instance,
<T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
compiles, but
Comparable<?> max(Comparable<?> a, Comparable<?> b) {
return a.compareTo(b) > 0 ? a : b;
}
does not, because the latter may be called by
max(Integer.MAX_VALUE, "hello");
The method body may refer to the actual type used by the caller using a type parameter, but not using a wildcard type. For instance:
<T extends Comparable<T>> T max(T... ts) {
if (ts.length == 0) {
return null;
}
T max = ts[0];
for (int i = 1; i < ts.length; i++) {
if (max.compareTo(ts[i]) > 0) {
max = ts[i];
}
}
return max;
}
compiles.
#Mark #Joachim #Michael
see the example in JLS3 5.1.10 Capture Conversion
public static void reverse(List<?> list) { rev(list);}
private static <T> void rev(List<T> list){ ... }
so the <?> version can do anything the <T> version can do.
this is easy to accept if the runtime is reified. a List<?> object must be a List<X> object of some specific non-wildcard X anyway, and we can access this X at runtime. So there's no difference using a List<?> or a List<T>
With type erasure, we have no access to T or X, so there's no difference either. We can insert a T into a List<T> - but where can you get a T object, if T is private to the invocation, and erased? There are two possibilities:
the T object is already stored in the List<T>. so we are manipulating elements themselves. As the reverse/rev example shows, there's no problem doing this to List<?> either
it comes out-of-band. There's other arrangement made by the programmer, so that an object somewhere else is guaranteed to be of type T for the invocation. Unchecked casting must be done to override compiler. Again, no problem to do the same thing to List<?>