Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A few Java Generics questions

Tags:

java

generics

I'm currently studying Java Generics from a book and there are a few things I don't quite understand.

Suppose I have a parameterized class:

class MyClass<T> { 

    T item;

    public T getItem() { return item; }

    // ...
}

I understand it is forbidden to create arrays of a parameterized type because of type erasure. Now the book says that I can use casts like so:

MyClass<String>[] foo = (MyClass<String>[]) new MyClass<?>[10];

but what is the difference to the following statement?

MyClass<String>[] foo = new MyClass[10];

In both cases at runtime the JVM only knows that foo is of the raw type MyClass[] right?

Now the book continues to say:

"The result is not safe. If you store a MyClass<OtherType> in foo[0] and then call a String method on foo[0].getItem() you get a ClassCastException. TIP: If you need to collect parameterized type objects, simply use an ArrayList: ArrayList<MyClass<String>> is safe and effective."

In what way is the usage of ArrayList safer in this case? I can produce a ClassCastException by storing a MyClass<OtherType> in the list just as easily.

Edit: I'm not really satisfied with any of the answers. Still the question remains: In what way is the usage of an ArrayList safer here? Can anyone give me specific examples that demonstrate that improved safety?

Taking up the following comment to my initial unedited post:

"I think that either the book doesn't explain this well, or you've taken the quote out of context. The problem is when you try to create an array of a generic argument type, e.g. inside MyClass you do T[] items = (T[]) new Object[10];"

What's the problem with that?

// Please don't comment about any problems related to bounds etc. This class should serve purely 
// for demonstration of the core issue I'm trying to understand
public class MyClass<T> {

    private T[] items = (T[]) new Object[10];

    private int size = 0;

    public void addItem(T item) {
        items[size++] = item;
    }

    public T getItem(int index) {
        return items[index];
    }
}

In what way specifically am I losing any type safety that an ArrayList can provide? Please note I'm not trying to make a case for using arrays with parameterized types, I'm sure that Collections in fact do perform better, I'm just trying to understand why they do and what problems arise from using arrays.

like image 416
yakmiras Avatar asked Mar 13 '26 20:03

yakmiras


1 Answers

The reason why is "safer" to use generic Collections vs naked arrays is because the compiler adds extra reassurance that your code will do what is intended as it is less likely that you will mix references to objects created with different type-arguments due to a coding error from your part.

For example it is totally legal to add a mixture of MyClasses into a MyClass array:

MyClass<Integer> mcInt = new MyClass<Integer>();
MyClass<String> mcString = new MyClass<String>();
MyClass[] array = new MyClass[] { mcInt , mcString }; 
...
MyClass<String>[] typedArray = (MyClass<String>[]) array;

The above may compile ok (depending as to how you handle unsafe warnings), however, typedArray will contain an invalid reference to a MyClass<Integer> typed element.

With generic collections the equivalent code won't ever compile because the compiler, thanks to the added type safety provided by using generics, realizes that you are adding that invalid reference.

MyClass<Integer> mcInt = new MyClass<Integer>();
MyClass<String> mcString = new MyClass<String>();
List<MyClass<String>> list = new ArrayList<MyClass<String>>(); 
list.add(mcString); // is ok.
list.add(mcInt); // won't compile.

You are welcome to use naked array but by doing that you are losing additional checks done by the compiler. In practice is is ok to use the unsafe arrays if these are private fields or local variables, you are extra careful and silence warnings using @SuppressWarnings("unchecked") when you are sure that your code is actually safe as far as the compiler would assured it to be if you were to use generics.

In my experience, most of the time is best just to use generic collections whenever you can but sometimes you may get in situations in where the unsafe alternative is far more convenient due to performance or just because the safe alternative results in more cumbersome code.

like image 100
Valentin Ruano Avatar answered Mar 16 '26 08:03

Valentin Ruano



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!