Generics in Java are often called pseudo-generics because the actual Type parameters are Type Erasure when the actual code is run.

So what led to this compromise in Java?

Here I will take you to the Java language designer’s point of view, lead you to understand the bitter past here.

What is a true generic

Before we look at Java “pseudo-generics”, let’s briefly discuss the difference between “real generics” and “pseudo-generics”.

True generics: Types in generics are real.

Pseudo-generics: Type checking only at compile time, erasing type information at run time.

1. A general idea for programming languages to implement “true generics”

Most programming languages introduce generics through compile-time bloat.

Take the implementation of “true generics” in Java language as an example. First, special encoding is used for generic types (generic classes, generic interfaces) and generic method names. For example, the Factory class is generated into a class named “Factory @@t”. To determine whether it is generic or not.

A method where placeholder T is used can be temporarily generated as an Object with a special Annotation to let the Java compiler know that it is a placeholder. Then, if the use of Factory

is found at compile time, copy all the logic of “Factory @@t”, create a new “Factory@String@” class, and replace the original placeholder T with String. Then generate new Factory@String@() when compiling new Factory

().

2. Examples of Chinese meanings of terms

Parameterized Type List of parameter types

Actual type parameter Indicates the Actual type parameter String

Formal Type Parameter Formal type parameter E

Taking Factory and Factory as examples, the generated code is as follows:

Factory<String>() f1 = new Factory<String>(); Factory<Integer>() f2 = new Factory<Integer>(); Factory<String>() f1 = new Factory@String@() Factory<Integer>() f2 = new Factory@Integer@();Copy the code

Where T in the Factory@String@ class has been replaced with String.

Where T in the Factory@Integer@ class has been replaced with Integer.

This is because generic types with different actual type parameters are replaced with different classes, and the types in the generic types are validated.

So we can do this in our program:

class Factory<T> { T data; public static void f(Object arg){ if(arg instanceof T){... }//success T var = new T(); //success T[] array = new T[10]; //success } }Copy the code

Problems with using true generics directly

Technically, Java is 100% capable of what we call “true generics.” To implement true generics, Java must deal with two things:

  • Modify the JVM source code so that the JVM can correctly read and verify generics information (previously Java did not have the concept of generics, so need to change).

  • To be compatible with older programs, you need to add a set of generic apis (mainly container types) that do not support generics in parallel.

ArrayList, for example, has to do this:

ArrayList<T>Copy the code

Even when all of the above is done, Java cannot adopt this solution.

Consider the following:

If I have A Java 5 project and A third-party B1 lib, where A project references an ArrayList in B1 lib, with the Java upgrade, the developer of B1 Lib in order to use the new Java feature – generics, So if the code is migrated to Java 5 and reborn as B2 Lib, then the A project must be upgraded to Java 5 and the code changed at the same time to be compatible with B2 Lib.

Why does the A project have to upgrade Java 5?

In Java does not support the high version of the Java compiler generates a class file to run on a low version of the JRE, if you try to do so, will get UnsupportedClassVersionError error. As shown below:

Therefore, to adapt to B2 Lib, project A must upgrade Java to Java 5.

Now let’s go back and think about how many open source frameworks are out there and how many projects have forced developers to change their code in order to introduce generics.

In this case, class, imagine for yourself. There are probably tens of thousands of developers with knives in their hands, blocking the door of Java language architects.

Mandatory type erasure

In the previous section, we explored the practical reasons why Java cannot directly introduce “true generics.” Because of the introduction of “true generics,” it was inevitable to add a generic API in parallel to an API that did not support generics. And the addition of API, for Java developers, must do migration.

So what else can developers do to smooth the transition to Java 5 and use the new features of generics?

Yes, yes. Java if you want to get rid of the migration problems of new versions of users. Java needs to do two things:

Instead of adding a generic API, you generalize existing types in situ.

Handles type compatibility before and after generalization.

Let’s discuss the purpose and reasons for doing these two things.

The first thing to do is to ensure that developers do not make changes to old code because of Java upgrades. In the case of ArrayList, changes are made directly under the original package (java.util), which looks like this:

//:point_down:Java old version class ArrayList{} //:point_down:Java5 generic version class ArrayList<T>{}Copy the code

The second thing I want to do is to use our previous example. In project A, project A references the ArrayList in B1 lib (using the list variable), so if project A upgrades to Java 5, it still references the B1 lib. Then the following situation must occur:

In the following code, the A project passes the generalized ArrayList

to the ArrayList in B1 lib.

ArrayList list = new ArrayList<String>();
ArrayList<T>Copy the code

When this happens, it leads to a problem. The ArrayList in the b1 project does not know that the ArrayList in the A project has been genericized. How to ensure that the genericized ArrayList is equivalent to the old version of the ArrayList?

If we follow the “true generics” approach to Java generics, then new ArrayList() will actually be replaced with new ArrayList@String@(), and the actual code to run looks like this:

ArrayList list = new Factory@String@()Copy the code

From the point of view of the code logic, it doesn’t work. Because ArrayList is not in the same class as ArrayList@String@, so what?

The most straightforward solution is to stop creating new classes for parameterized types and replace all the type parameters in the generic type with Object at compile time. (Since no new classes are created, the type T in the generic class can only be replaced with Object.)

In the actual implementation of generics in Java, the choice of whether to replace a boundary or Object is based on whether a type parameter in a generic type has a boundary.

Here’s an example:

In the code above, you declare a generic type Node, which is actually Node after the compiler replaces it. And that’s it:

// compiler code Node Node = new Node<String>(); // Compiled code Node Node = new Node();Copy the code

Through the magic of the compiler, Java solves the problem of dealing with older versions of generics.

By now, I’m sure you understand why generics in Java erase type information. Although Java’s “pseudo-generics” have long been stigmatised by other programming languages, it is still respectable and acceptable to be compatible with older versions.

Whatever Java does, it always does more good than bad. Here’s a video for you. After watching this video, you will know how important Java is to the whole world.

Wenyuan network, only for the use of learning, such as infringement, contact deletion.

I’ve compiled the interview questions and answers in PDF files, as well as a set of learning materials covering, but not limited to, the Java Virtual Machine, the Spring framework, Java threads, data structures, design patterns and more.

Follow the public account “Java Circle” for information, as well as quality articles delivered daily.