preface

This is a question I felt I didn’t answer well when I was in tik Tok 2. Because I only focused on the problem of T being replaced by Object, and did not explain the problems it would bring.

Mind mapping

What is generic erasure?

This is a very common question. You might even use it a lot, but you don’t pay attention to it, but it just so happens that it’s easy for the interviewer to catch it. Let’s start with a piece of code.

List list = new ArrayList();
List listString = new ArrayList<String>();
List listInteger = new ArrayList<Integer>();
Copy the code

These pieces of code are simple, rough, and very familiar. So WHAT I’m going to do is I’m going to insert a 1 into these three different pieces of code.

You, the reader, are probably black by now ???? You must have a lot of questions. This is obviously different.

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList();
        List listString = new ArrayList<String>();
        List listInteger = new ArrayList<Integer>();

        try {
            list.getClass().getMethod("add", Object.class).invoke(list, 1);
            listString.getClass().getMethod("add", Object.class).invoke(listString, 1);
            // For unconvinced readers, you can try a string instead.
            listInteger.getClass().getMethod("add", Object.class).invoke(listInteger, 1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("list size:" + list.size());
        System.out.println("listString size:" + listString.size());
        System.out.println("listInteger size:"+ listInteger.size()); }}Copy the code

To explore the truth

The above is just one manifestation of generic erasure, but for a better understanding, of course you need to go deeper. Although the List is large, it is not impossible to look at.

Two key points to verify:

  1. Data store Type
  2. Data acquisition
// Let's look at the List with a big pie
// We can see the generic E very clearly
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess.Cloneable.java.io.Serializable{       
    // The first key point
    // The type of storage that failed before it started
    // Shouldn't it also be a generic E?
    transient Object[] elementData;

    
    public E get(int index) {
        rangeCheck(index);

        return elementData(index); / / 1 -- -- -- -- >
    }
    
    // The function called directly by 1
    // The second key point is to force the converted data
    E elementData(int index) {
        return(E) elementData[index]; }}Copy the code

I think, as you can see, this so-called generic T will eventually be converted to an Object, and then it will be forced into a transformation. From this we can see why String data can be received directly by Integer when our data comes in from above.

What kind of problems?

(1) Forced type conversion

The result of this problem, which we have already mentioned in the previous article, is that when we insert by reflection, our data will get an error.

If we unknowingly insert a String value in a List

, who are we to talk to about such a big mistake?

(2) Reference transfer problem

In the above question, we have already said that T will be escaped to Object later. Does it make sense to convert the reference as well?

List<String> listObject = new ArrayList<Object>();
List<Object> listObject = new ArrayList<String>();
Copy the code

If you write like this, during our inspection phase, an error will be reported. But in a logical sense, are you really wrong?

Assuming that our first solution is correct, we simply store a bunch of Object data and then force it to a String, which sounds perfectly ok because the way we store data in a List is Object. The problem is ClassCastException, because Object is the base class of everything, but strongcasting is a way for subclasses to get to their superclass.

Suppose our second option is correct, at this time, according to the data String above the store, but what is the meaning of existence? In the end it’s all going to be Object, so you might as well just be Object.

The solution

It’s actually pretty simple, and if you’ve seen some of the public lectures you’ve seen this.

public class Part<T extends Parent> {

    private T val;

    public T getVal(a) {
        return val;
    }

    public void setVal(T val) {
        this.val = val; }}Copy the code

Compared to the previous Part, there is a

statement. This is a rearrangement of the base class, and even if compiled, the virtual machine will know to convert the data to Parent instead of replacing it directly with Object.

Application scenarios

Here I want to thank the big guy readers who gave me the question: excavator technology

The idea for this section comes from the distinction between extends and super in Java generics.

Above we said the solution is to use

. In fact, this is just a scheme, in different scenarios, we need to add different methods of use. In addition, this method is officially advocated, but we need to give some scenarios to avoid the mistakes mentioned above.

It’s based on two scenarios, one is extended super and the other is inherited extends. Here’s a list of examples.

Unified succession

/ / carrier
class Plate<T>{
    private T item;
    public Plate(T t){item=t; }public void set(T t){item=t; }public T get(a){return item;}
}

// Lev 1
class Food{}

// Lev 2
class Fruit extends Food{}
class Meat extends Food{}

//Lev 3
class Apple extends Fruit{}
class Banana extends Fruit{}
class Pork extends Meat{}
class Beef extends Meat{}

//Lev 4
class RedApple extends Apple{}
class GreenApple extends Apple{}
Copy the code

<T extends Parent>

What’s the use of inheritance?

In fact, what he expects is that the base of the entire list of data will come from our Parent, so that all of the data will come from our Parent, so you can call this list the Parent family. So you can say that this is a solution suitable for frequent reads.

Plate<? extends Fruit> p1=new Plate<Apple>(new Apple());
Plate<? extends Fruit> p2=new Plate<Apple>(new Beef()); // Check failed

// Failed to modify data
p1.set(new Banana());

// All data is available
// But he can only pinpoint Fruit as defined by us
Fruit result = p1.get();
Copy the code

<T super Parent>

What’s the role of extensibility?

You can think of it as a compatibility tool, with the super modifier indicating compatibility with the class, which is more suitable for storing the data in the Parent list above. This is a scheme suitable for frequent insertion.

// Fill in the position of Food, the level must be greater than or equal to Fruit
Plate<? super Fruit> p1=new Plate<Food>(new Apple());
// Unlike extends, it can be stored
p1.set(new Banana());
/ / get methods
Banana result1 = p1.get(); // An error will be reported, which must be cast, because only an Object is returned
Object result2 = p1.get(); // Return an Object. We are about to lose all the data, so it is not suitable to read
Copy the code

The above is my learning results, if there is anything I did not think about or there is a mistake in the article, welcome to share with me.


Related articles recommended:

【 Audio and video development from Zero impact 】 The basic knowledge of audio and video development

Promise me, keep this dingding and Douyin profile, it’s really important!!

How do you reproduce the three classic synchronization problems in operating systems?