Explain the implementation principle of generics in Java in detail

  • 2021-09-05 00:04:42
  • OfStack

Directory generics
Generic method
Generic class
Generic interface
Fundamentals of generics
Summary
Reference

Generics is a common technology in Java development. Understanding several forms of generics and the basic principles of implementing generics will help to write better code. This paper summarizes three forms of Java generics and the implementation principle of generics.

Generic type

The essence of generics is to parameterize types, which are used when code logic is not concerned with specific data types. For example, to implement a general sorting algorithm, the focus is on the algorithm itself, rather than the sort of object type.

Generic method

The following defines a generic method and declares a type variable that can be applied to parameters, return values, and code logic within the method.


class GenericMethod{
 public <T> T[] sort(T[] elements){
  return elements;
 }
}

Generic class

Similar to generic methods, generic classes also need to declare type variables, except that they are placed after the class name, and the scope of action includes the current member variable types, method parameter types, method return types, and the code within the method.

When a subclass inherits a generic class or instantiates an object of a generic class, you need to specify a specific parameter type or declare a parameter variable. As follows, SubGenericClass inherits the generic class GenericClass, where the value of the type variable ID is Integer, and the subclass declares another type variable E, and fills E in the parent class declaration T.


class GenericClass<ID, T>{
 
}

class SubGenericClass<T> extends GenericClass<Integer, T>{
 
}

Generic interface

Generic interfaces, like generic classes, also require declaring type variables after the interface name, which act on abstract method return types and parameter types in the interface. Subclasses need to fill in specific data types or type variables declared by subclasses when implementing generic interfaces.


interface GenericInterface<T> {
 T append(T seg);
}

Fundamentals of generics

The essence of generics is to parameterize data types, which is realized by erasing. The. java source code for the generics is declared, and after the. class file is compiled, the generics-related information disappears. It can be said that the generic-related information in the source code is provided to the compiler. Generic information is visible to the Java compiler but not to the Java virtual machine.

The Java compiler implements erasure in the following ways:

Instead of generics with Object or defined types, the resulting bytecode contains only the original classes, interfaces, and methods; Insert cast code in place to ensure type safety; Insert bridging methods into classes that inherit generic classes or interfaces to preserve polymorphism.

Original text of Java official document

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
Insert type casts if necessary to preserve type safety.
Generate bridge methods to preserve polymorphism in extended generic types.

Type erasure in Java is explained by specific code below.

Principle of experiment: firstly compile. java file into. class file with javac, then reverse compile. class file into back Java code with decompilation tool jad, and the decompiled Java code content reflects the information in. class file.

The following source code, the definition of User class, the implementation of Comparable interface, type parameters filled in User, compareTo method.


class User implements Comparable<User> {
 String name;
	
 public int compareTo(User other){
  return this.name.compareTo(other.name);
 }
}

The source code of Comparable interface in JDK is as follows:


package java.lang;
public interface Comparable<T>{
 int compareTo(T o);
}

We first decompile its interface, the ByteCode file for the Comparable interface, which can be found in $JRE_HOME/lib/rt. jar, and copy it to a directory. Decompile the Comparable. class file using jad. exe (additional installation required).


$ jad Comparable.class

The decompiled contents are placed in the Comparable. jad file, which reads as follows:


// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: Comparable.java

package java.lang;

// Referenced classes of package java.lang:
//   Object

public interface Comparable
{

 public abstract int compareTo(Object obj);
}

Comparing the contents of source code Comparable. java with decompiled code Comparable. jad, it is not difficult to find that the type variable T is no longer in the decompiled content. The parameter type T in the compareTo method is also replaced by Object. This is in line with the first erase principle mentioned above. Here is to use Object to replace the type parameter, using the demarcated type to replace the type parameter example can be decompiled under 1 Collections. class try, which uses a large number of generics.

User. java is compiled into the. class file using javac. exe, and the. class file is decompiled into the Java code using jad.


$ javac User.java
$ jad User.class

The User. jad file reads as follows:


// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: User.java


class User
 implements Comparable
{

 User()
 {
 }

 public int compareTo(User user)
 {
  return name.compareTo(user.name);
 }

 //  Bridging method 
 public volatile int compareTo(Object obj)
 {
  return compareTo((User)obj);
 }

 String name;
}

Comparing the edited source code User. java with the decompiled code User. jad, it is easy to find that there is no type parameter, one more parametric construction method and one more compareTo (Object obj) method, which is the bridging method. It can also be found that the parameter obj is strongly converted into User and then passed into compareTo (User user) method. From this, you can see the implementation of Erase Rule 2 and Rule 3.

Forced conversion rules are easy to understand, because generics have been replaced by Object. To call a method or member variable of a specific type, of course, you need to forcefully convert to a specific type before you can use it. So how to understand the bridging method of insertion?

If we only use the User class in the following way, we really don't need a bridge method with parameter type Object.


User user = new User();
User other = new User();
user.comparetTo(other);

However, the polymorphism in Java allows us to use a reference to a parent class or interface to point to a subclass object.


class GenericClass<ID, T>{
 
}

class SubGenericClass<T> extends GenericClass<Integer, T>{
 
}
0

According to the principle of replacing generic parameters with Object, there are only compareTo (Object) methods in Comparable interface. Assuming there is no bridging method, it is obvious that the following code cannot run. Therefore, the Java compiler needs to generate an additional bridge method for generic methods in subclasses (subclasses of generic classes or implementation classes of generic interfaces), so as to ensure the polymorphism in Java.


class GenericClass<ID, T>{
 
}

class SubGenericClass<T> extends GenericClass<Integer, T>{
 
}
1

Generic methods in ordinary classes do not produce bridging methods when erasing types. For example:


class GenericClass<ID, T>{
 
}

class SubGenericClass<T> extends GenericClass<Integer, T>{
 
}
2

After type erasure, it becomes:


class Dog
{

 Dog()
 {
 }

 void eat(Object aobj[])
 {
 }
}

Summary

There are three forms of generics in Java: generic methods, generic classes and generic interfaces. Java implements generics through type erasure at compile time. Object or defined types are used instead of generics when erasing, and strong-spin code is inserted when calling specific type methods or member variables. To ensure polymorphism, Java compiler will also generate bridge methods for subclasses of generic classes. After the type information is erased at compile time, the program cannot get the specific type corresponding to the type parameter at run time.

Reference

https://docs.oracle.com/javase/tutorial/java/generics/index.html

https://stackoverflow.com/questions/25040837/generics-bridge-method-on-polymorphism

The above is the detailed explanation of Java generic implementation principles in detail, more about Java generic implementation principles of information please pay attention to other related articles on this site!


Related articles: