Explain generic erasure? Why does java have to force?

1. Overview:

Before explaining what generic erasure is, we must first understand what Java generics are. The so-called generic is a parameterized type. This means that we can pass concrete types as a parameter to methods, classes, and interfaces.

Why do we need generics? First of all, we all know that in java, Object is the parent class of the object. Object can refer to any type of object. But this will bring type safety problems. The emergence of generics brings the function of type safety to java.

1. What are the benefits of using generics?

  • Guaranteed type safety and compile-time error checking to make the code more secure and readable.
  • No type casting is required.

For the following program, if you do not use generics before, it is easy to make mistakes by forcing the result:

public class GenericClient {<!-- -->

    public static void main(String[] args) {<!-- -->
        // Don't use generics, the value type passed in the collection will not be restricted
        // There is no limit to storing values, and it is easy to make mistakes when fetching values, and the security of the container cannot be guaranteed
        List list = new ArrayList();
        list.add("tom");
        list. add(11);
        for(Object obj : list){<!-- -->
            // Data fetching requires forced conversion, which reduces code readability and usability
            String str = (String) obj;
            System.out.println(str);
        }
    }
}

A type conversion error occurred:

tom
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at com.koo.generic.GenericClient.main(GenericClient.java:22)

If generics are used, the compiler checks to avoid known type conversion exceptions:

2. How to use generics

2.1 On classes

class Test<T> {<!-- -->
T obj
Test(T obj){<!-- -->
this.obj = obj;
}
}

2.2 In method

class Test {<!-- -->
static <T> void helloworld(T t){<!-- -->}
}

2.3 On the interface

interface Test<T> {<!-- -->
T getData();
}

2. What is type erasure?

1. Concept

The implementation of Java generics is at the compilation layer, and the bytecode generated after compilation does not contain the type information in generics. Therefore, when using generics, the added type parameters will be removed when the compiler compiles. This process is called type erasure.

2. Erase example

public static <T> boolean myequal(T t1, T t2){<!-- -->
return t1. equals(t2);
}

The compiler will replace the type T with Object, as follows:

public static <Object> boolean myequal(Object t1, Object t2){<!-- -->
return t1. equals(t2);
}

3. Type erasure of generic classes

Type erasure at the class level follows this rule: first the compiler discards the type parameter on the class and replaces it with its first bound type, or Object if the type parameter is not bound.

  • parameter type is not bound
public class MyClass<T> {<!-- -->
private T[] elements;
public void doSomething(T element){<!-- -->}
public T getSomething(){<!-- -->}
}

The type parameter T of MyClass is not bound to any type, so T will be replaced by Object, and the result of the replacement is:

public class MyClass {<!-- -->
private Object[] elements;
public void doSomething(Object element){<!-- -->}
public Object getSomething(){<!-- -->}
}
  • The parameter type is bound
interface MyT {<!-- -->}
public class MyClass<T extends MyT> {<!-- -->
private E[] elements;
public void doSomething(T element){<!-- -->}
public T getSomething(){<!-- -->}
}

MyTClass is the first type to which the type parameter T of MyClass is bound, so T will be replaced by MyTClass:

public class MyClass {<!-- -->
private MyT[] elements;
public void doSomething(MyT element){<!-- -->}
public MyT getSomething(){<!-- -->}
}

Why is it OK to take the first binding? For example, if MyT also has a parent class, and the parent class has a parent class, then our type parameters have many indirect bindings, and the first binding covers all parent classes, so use the first Just bind it.

4. Type erasure of generic methods

For a generic method, its type parameters will not be stored, it follows this rule: first the compiler discards the type parameter on the method, and replaces it with its first bound type, if the type parameter is not bound, Just use Object to replace.

  • parameter type is not bound
public static <T> void printSomething(T[] arr){<!-- -->
for(T item: arr) {<!-- -->
System.out.printf("%s", item);
}
}

The above method, after performing type erasure results:

public static void printSomething(Object[] arr){<!-- -->
for(Object item: arr) {<!-- -->
System.out.printf("%s", item);
}
}
  • The parameter type is bound
public static <T extends MyT> void printSomething(T[] arr){<!-- -->
for(T item: arr) {<!-- -->
System.out.printf("%s", item);
}
}

The above method, after performing type erasure results:

public static void printSomething(MyT[] arr){<!-- -->
for(MyT item: arr) {<!-- -->
System.out.printf("%s", item);
}
}

3. Problems with type erasure

Problem Description:

The type information is erased, how can you guarantee that only the limited type of the generic variable is used? After compilation, String is Object, and Integer is also Object. How can we determine which one to use?

1. Check for references, not reference objects

What are citations?
For example A a = new A();
At this time, the variable a points to an A object, and a is called a reference variable. It can also be said that a is a reference to the A object. We manipulate the A object by manipulating the reference variable a. The value of variable a is the address of the object it refers to.

In order to ensure the correct use of types, the implementation order of java is as follows:
First check the generic type (for reference) – type erasure – compile

As shown in the picture:

Type checking is done at compile time. new ArrayList() just opens up a storage space in memory, which can store any type of object. What really involves type checking is its reference, because we use its reference to call its method. For example, list1 calls the add() method, which is generically qualified, so list1 reference can complete the generic type check . The reference list2 does not use generics, so no type checking is performed.

Through the above example, we can understand that type checking is for references. Whoever is a reference, using this reference to call a generic method, will perform type detection on the method called by this reference, regardless of the object it actually refers to.

2. Parameterized types in generic types do not consider inheritance relationship

Passing by reference is not allowed in the following cases – the collection does not have an inheritance relationship between types

3. Generic type variables cannot be basic data types

A type parameter cannot be substituted for a primitive type. For example, there is no ArrayList, only ArrayList. Because when the type is erased, the original type of ArrayList becomes Object, but the Object type cannot store double values, and can only refer to Double values.

4. Runtime type checking exception – instanceof

For example:

 ArrayList<String> arrayList = new ArrayList<String>();

Because after type erasure, only the original type remains in ArrayList, and the generic information String does not exist.
So you can’t judge like this:

if(arrayList instanceof ArrayList<String>){<!-- -->}

The correct way is to use wildcards:

if(arrayList instanceof ArrayList<?>){<!-- -->}

Source code download:
https://gitee.com/charlinchenlin/koo-erp