WeiChing 林煒清
WeiChing 林煒清

Reputation: 4469

Why cast to generic type takes effect?

On Java revisited ,a code excerpt goes below:

class Holder<T>{
  private T[] contents;
  private int index = 0;
  public Holder(int size){
    //contents = new T[size]; //compiler error - generic array creation
    contents = (T[]) new Object[size]; //workaround - casting Object[] to generic Type
  }...}

It is for generic array creation, but according to type erasure (I checked it on java online tutorial), T ends into Object at class compiled, so the cast (T[]) would ends into (Object[]), and that seems take no difference to without casting.

So what is the function of that casting or any special meaning for casting? any hint is thankful.

Upvotes: 2

Views: 187

Answers (4)

Rohit Jain
Rohit Jain

Reputation: 213223

The cast is required to tell the compiler that the assignment is Ok from our side. Else it will show you compiler error.

Due to type erasure, the type parameter T is replaced with it's erasure at compile time. The erasure of an unbounded type parameter is Object, whereas the erasure of the bounded type parameter is the type denoting the upper bound. So, if your type parameter in class had an upper bound - Holder<T extends Number>, then the erasure of T will be Number. That means, T will be replaced with Number by the compiler.

So, in this case, since T in unbounded, its erasure is Object. So, it is replaced with Object by the compiler.

Even though the casting removes the compiler error, the compiler would still show you a warning of Unchecked Cast. Because, the cast is not type safe, and would fail at runtime with ClassCastException in case you instantiate the generic type using String type parameter.

Try this:

Holder<String> stringHolder = new Holder<>(5);
String[] contents = stringHolder.getContents();  // ClassCastException

A safer way to create generic array is using Array.newInstance method. You need to pass Class<T> parameter to your constructor, and then use the following code:

public Holder(int size, Class<T> clazz){
    contents = (T[]) Array.newInstance(clazz, size);
}

Here also you will see the Unchecked Cast warning. But that is harmless.

Even safer way is not at all to create a array whose component type is a type parameter. You can rather use an ArrayList<T> instead.


Reference:

Upvotes: 4

Mario Rossi
Mario Rossi

Reputation: 7799

If you consider that after type erasure all type parameters end up as simple Objects, your question is about the use of Java Generics as a whole. In my opinion, these are:

  1. Documentation / inter-developer communication / legibility / maintainability.
  2. Avoid logic errors as early as possible (i.e. statically).
  3. Reduce testing requirements / allow to refocus testing efforts in more transcendental aspects and made the code more robust.

Unfortunately, it was difficult to fit Generics in Java maintaining full backwards compatibility and other objectives at that point. The result is that new T[size] is invalid. The only alternative is new Object[size]. The cast (T[]) is to tell the compiler and other developers that that's exactly what you intended. In particular, the compiler will not emit a warning message in this particular line (which is much safer than globally disabling this kind of checks).

Upvotes: 0

sanbhat
sanbhat

Reputation: 17622

The cast is present to get rid of compilation error. And its an unchecked way of creating generic array.

Even the ArrayList follows the same method. You can have a look at this answer to see both checked and unchecked methods

Upvotes: 1

MrRaymondLee
MrRaymondLee

Reputation: 536

T is just a placeholder. You can't instantiate it which is while the compile error exists. At compile time you need to tell the compiler what the placeholder presents. In the case of the code excerpt, the placeholder is anything.

Upvotes: 1

Related Questions