Home > Software engineering >  Type erasure Generics
Type erasure Generics

Time:05-01

An error occurs at new T[5] during compile-time saying => error: generic array creation and according to my understanding, the array is created during compile-time and since we don't know the type of T at compile-time we cannot instantiate an array. But if T gets erased at compile-time and changes to Object then still why this error occurs ? because we can create an array of Object.

// Before Compiling 
public class GenericClass<T> {
    GenericClass(){
        T[] obj = new T[5];
    }
}

// After Compiling
public class GenericClass {
GenericClass() {
Object[] obj = new Object[5];
}
}

Similar case, like,

public class GenericClass<T> {
        GenericClass(){
            T obj = new T(); }}

/*  error :required: class
  found:    type parameter T
  where T is a type-variable:
  T extends Object declared in class GenericClass*/

CodePudding user response:

according to my understanding, the array is created during compile-time

No, the array is created at runtime.

nd since we don't know the type of T at compile-time we cannot instantiate an array.

Correct.

But if T gets erased at compile-time and changes to Object then still why this error occurs ?

Because "it is erased at compile time and changes to Object" is oversimplified.

Also, generics and arrays don't play nice with each other. The problem is, where the generics part is erased, arrays do not work like that. You can do this:

String[] x = new String[10];
tellMeTheTypeOfMyArray(x);

void tellMeTheTypeOfMyArray(Object[] o) {
  System.out.println("Your array's component type is: "   o.getClass().getComponentType());
}

This code will compile and work fine, without error, and prints:

Your array's component type is: java.lang.String

Contrast to generics where you cannot write such a method. You cannot possibly make this:

List<String> x = new ArrayList<String>();
tellMeTheTypeOfMyList(x);

void tellMeTheTypeOfMyList(List<?> o) {
  System.out.println("Your list's component type is: "   ??????);
}

work. There's no java code possible here, nothing you can write in place of the ?????? to print String, because that information simply is not there at runtime anymore.

Imagine this code:

// This is written by Wahab today.

class Example<T> {
  protected T[] arr;

  Example() {
    this.arr = new T[10];
  }
}

and it worked like you wanted. Then I do:

// Written by me, a year later
class Uhoh extends Example<String> {
  Uhoh() {
    super();
  }

  void hmmm() {
    System.out.println(this.arr.getComponentType());
  }
}

I would obviously expect, nay, demand - that this prints java.lang.String, but it could not possibly do so. Because this is weird and confusing, java has a rule: If you compile your code and you do not see any warnings about generics problems (and did not @SuppressWarnings them away), then this kind of confusion is not likely to happen.

Allowing you to write new T[] and having that just be a silly way to write new Object[] is considered too far gone for this.

So how do I use arrays with generics types?

The same way java.util.ArrayList does it: Do not use generics here. Arrays should pretty much never have T types if you intend to create them inside the generic code. If you have a T[] anywhere in your codebase, then that means you should never be new-ing up anything for it - let the caller of your code do it for you. If you do want to new up new arrays yourself, don't use T, use Object[] as type, and cast to T where needed. This is literally how java's built-in ArrayList class works. Some excerpts copy-pasted straight from its source:

    transient Object[] elementData; // non-private to simplify nested class access

    public E get(int index) {
        Objects.checkIndex(index, size);
        return elementData(index);
    }

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        return (E) elementData[index];
    }

Here's an example, again straight from ArrayList's sources (or rather, java.util.Collection defines this, and ArrayList inherits it), where you let the caller provide you with code to make arrays:

    default <T> T[] toArray(IntFunction<T[]> generator) {
        return toArray(generator.apply(0));
    }

Here the caller provides a function that transforms an int into a T[] - it takes the concept of doing new String[10] and turns it into a function, that you then pass along to the toArray method which will then use it (feel free to ignore how it uses it here, it's a bit of a bizarre solution. It works, just - not sure you should be learning lessons about that part).

You use it like this:

List<String> listOfStrings = ...;
String[] convertedToArray = listOfStrings.toArray(String[]::new);

CodePudding user response:

Java arrays know their component type at runtime. When you create an array, you must provide the component type at runtime. But in your GenericClass, it cannot do that because it does not know what T is at runtime. If it creates an Object[], that object will have the wrong runtime class, and that instance is not compatible with the type T[] if T is anything other than Object. You are correct that, within the class, nothing is immediately wrong. But if the claim that the variable is T[] is exposed to an outside scope which expects T to be a more specific type, it can cause a ClassCastException:

// Before type erasure 
class GenericClass<T> {
    T[] obj;
    GenericClass() {
        obj = new T[5]; // if hypothetically you could do this
    }
    T[] getObj() {
        return obj;
    }
}
class MyCode {
    public static void main(String[] args) {
        GenericClass<String> foo = new GenericClass<>();
        String[] strings = foo.getObj(); // no casts needed; no warnings
    }
}

// After type erasure
class GenericClass {
    Object[] obj;
    GenericClass() {
        obj = new Object[5];
    }
    Object[] getObj() {
        return obj;
    }
}
class MyCode {
    public static void main(String[] args) {
        GenericClass foo = new GenericClass();
        String[] strings = (String[]) foo.getObj(); // ClassCastException at runtime
    }
}
  • Related