Preslav Rachev
Preslav Rachev

Reputation: 4003

Confused by generics in Java

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

Answers (5)

Konstantin Yovkov
Konstantin Yovkov

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

dskfdskjgds
dskfdskjgds

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

ka4eli
ka4eli

Reputation: 5424

You missed diamond <> (this will not compile):

List<Long> longNums = new ArrayList<>(Arrays.asList("one", "two","three"));

Upvotes: 1

G_hi3
G_hi3

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

biziclop
biziclop

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

Related Questions