I have a code like this:
public class A<T extends String> {
T field;
List<T> fields;
public T getField() {
return field;
}
public List<T> getFields() {
return fields;
}
public static void test(){
A a = new A();
String s = a.getField();
List<String> ss = a.getFields();
}
}
Why when creating A
with no generic type getFiled
returns String
but getFileds
returns List<Object>
?
I have to define A
as A<String> a = new A<>()
for this to work properly.
Thanks,
The erasure of the type param T
in the declaration T field
is String
, because T
has bound:
<T extends String>
and any subtype
of a String
is also a String
1.
But, this doesn't apply to generic List<T>
. It stems from the fact that generics are not covariant:
List<String> str = new ArrayList<String>();
List<Object> obj = str; // Error: incompatible types (even though String extends Object)
The erasure of List<T>
in the declaration List<T> fields
is List
, particularly because List<T>
can hold a String
or its subtypes
1. And given that A
is a raw type, compiler can't guess the type of the elements in the list returned by the getFields()
method.
Anyway, this line of code:
List<T> fields;
is compiled (Erasure of Generic Types) into:
List fields;
As a result, when you write:
A a = new A(); instead of A<String> a = new A<>();
you lose the type safety and get an "Unchecked assignment" warning - in this case, compiler can not guarantee that the fields list
is exactly the List<String>
:
List<String> list = a.getFields(); // Unchecked assignment: 'List' to 'List<String>' ...
P.S. Don't use raw types!
1 - As you know, String
class is final and can't be extended. Replace it with any type that can be extended if you wish.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With