Reputation: 4003
Can anyone explain why the following piece of code compiles?
List<Long> longNums = new ArrayList(Arrays.asList("one", "two", "three"));
Is it due to type erasure?
If I have the following method:
public <T> T readProperty(String propName, Class<T> type);
How can I make sure that it will return, say a List<Long>
and not a List<String>
? Obviously, at the type of calling the method I can only supply List.class
and pray.
//run and pray
List<Long> longNums = readProperty("prop", List.class);
It has already happened to me that such a method incorrectly assigned a list of String objects to a list of long number, and it wasn't until I ran it, that I saw the ClassCastException
.
Upvotes: 6
Views: 315
Reputation: 62854
You shouldn't expect the program to behave correctly, because you don't use Generics correctly.
First, you should not mix Generics with Raws, which means that this statement
List<Long> longNums = new ArrayList(Arrays.asList("one", "two", "three"));
is invalid (in terms of correctness). If it were:
List<Long> longNums = new ArrayList<Long>(Arrays.asList("one", "two", "three"));
it wouldn't even compile and you would get the error at compile-time (not at Runtime, which may be more confusing and scary).
So, in order to get compiled, your list should be defined as:
List<String> longNums = new ArrayList<>(Arrays.asList("one", "two", "three"));
Further, with regards to this statement
//run and pray
List<Long> longNums = readProperty("prop", List.class);
there actually no way to make sure that readProperty
will return List<Long>
, so you will have to either do a cast or add a @SuppressWarnings
annotation. The reason for this is the type-erasure feature of the compiler - the parameterized type Long
get erased and it's gone at Runtime (when actually the readProperty()
is executed and the value of prob
is obtained via Reflection).
Upvotes: 9
Reputation: 351
Generic class:
public abstract class GenericClass<T> {
private T managedClass;
public List<T> readListProp(String propName,T object){
//do stuff using T type instead of Long,String,Double,etc
}
Other classes:
public class FirstClass extends GenericClass<String>
{...
//you have a class where T from generic is replaced with String
}
public class SecClass extends GenericClass<Double>
{...
//you have a class where T from generic is replaced with Double
}
Upvotes: 0
Reputation: 5424
You missed diamond <>
(this will not compile):
List<Long> longNums = new ArrayList<>(Arrays.asList("one", "two","three"));
Upvotes: 1
Reputation: 598
If you define a List as ArrayList
, java doesn't know what kind of object are in it. You can assign it to an ArrayList<Long>
, but you'll get an error when you call methods or properties on items, because the type doesn't match.
Upvotes: 0
Reputation: 49724
These are two separate questions really.
List<Long> longNums = new ArrayList(Arrays.asList("one", "two", "three"));
For source compatibility reasons a raw type is allowed in these cases. You shouldn't rely on this because as you can see, it defeats the whole idea of generics.
As for the second question, you can sidestep the issue by doing something like this:
public <T> List<T> readListProperty(String propName, Class<T> type);
...
List<Long> longNums = readProperty("prop", Long.class);
It's more of a workaround than a solution and isn't always applicable. (For example when you need even more complex return types, like Map<String,List<String>>
)
Upvotes: 1