13_Java generics summary

Posted by ghe on Wed, 05 Jan 2022 05:47:18 +0100

1. Generic overview

1.1 understanding generics

  • Generic, that is, parameterized types.

  • The essence of generics is to parameterize types

  • Parameterized type, that is, the type is parameterized from the original specific type, which is similar to the variable parameters in the method. At this time, the type is also defined as a parameter form (which can be called a type parameter), and then a specific type (type argument) is passed in during use / call

  • Before generics, we can only define a collection of type Object, so that the collection can store objects of any data type

    Then, after we use the collection, we must clearly know the data type of the storage elements in the collection, otherwise it is easy to cause ClassCastException exceptions. At this time, we can only use forced type conversion

  • However, with generics, we use generics when defining classes, which also allows the class to store arbitrary data type objects

    When using this class, the data type accessed in this class is determined by assigning a value to the generic type. In this way, if the data type stored is incorrect, an error will be reported directly in the compilation to remind the programmer of writing an error

1.2 benefits of generics:

As mentioned above, the benefits of generics are:

  1. Type safety
  2. Elimination of cast type conversion

1.3 generic identifier

  • E - Element (used in a collection because the collection holds elements)
  • T - Type (Java class)
  • K - Key
  • V - Value
  • N - Number (numeric type)
  • ? - Represents an uncertain java type. This is only used when using generic types. It is an argument. The above are used when defining
  • When defining generic classes or methods, you can directly use these generic identifiers as specific data types

2. Generic classes and interfaces

2.1 defining generic classes and interfaces

  1. Generic class definition syntax:
class Class name <Generic identity, Generic identity...> {
	private Generic identification variable name;
	...
}
  1. Definition syntax of generic interface:
interface Interface name <Generic identity, Generic identity...> {
	Generic identity method name(); // Method whose return value is generic
    ...
}

2.2 using generic classes and interfaces

  • Usage syntax of generic class objects:
Class name<Specific data types> Object name = new Class name<Specific data types>();
// From jdk1 7. The specific data types in the following < > can be omitted without writing:
Class name<Specific data types> Object name = new Class name<>();

// Example: using collection classes
List<String> list = new ArrayList<>();
  • Derived subclasses of generic classes:

    1. If the subclass is also a generic class, the generic types of the parent class and the subclass should be the same (can be expanded):

    class Child<T> extends Parent<T>
    

    2. If the subclass is not a generic class, specify the specific data type of the parent class generic when inheriting the subclass:

    class Child extends Parent<String>
    
  • Use of generic interfaces (implementation classes):

    1. If the implementation class is also a generic class, the generic types of the implementation class and the interface should be consistent (can be expanded):

    class Child<T,E> implement Interface name<T> { // Example of capacity expansion
    	// You can define generic variables and implementation methods   
    }
    

    2. If the implementation class is not a generic class, specify the data type of the interface generic:

    class Child implement Interface name<String>
    

Precautions for generic classes:

  1. If no specific data type is specified when defining an Object using a generic class, the default data type is Object

  2. The type parameter of a generic type can only be a class type, not a basic data type

  3. A generic type can logically be regarded as multiple different types, but they are actually the same type (type erasure will be performed before running)

    Therefore, you can't use method overloading with different generic types (which can also be understood as the same after type erasure)

  4. You cannot also use the instanceof operation on an exact generic type

  • Take part of the source code of ArrayList as an example:
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	public E set(int index, E element) {
        Objects.checkIndex(index, size);
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }

	private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }
}

3. Generic methods

3.1 why use generic methods

  • The generic type defined by the generic class is valid in the whole class.
  • After the object of the generic class specifies the specific type to be operated on, all the types to be operated on have been fixed. Therefore, if the method is directly defined in the generic class at this time, the generic type in the method has already been determined
  • Therefore, generic methods are introduced. Generic methods can make methods change independently of classes. The following is a basic guiding principle for defining generics:

Whenever you can, you should try to use generic methods. That is, if you can generize the entire class using generic methods, you should use generic methods. In addition, for a static method, the parameters of generic types cannot be accessed. Therefore, if static methods want to use generic capabilities, they must be made generic methods.

  • Special features:

Static methods cannot access generics defined on a class
If the application data type of static method operation is uncertain, you can define generics on the method.

  • Differences between generic classes and generic methods:

    Generic class: Specifies the specific type of the generic when instantiating the class

    Generic method: Specifies the specific type of the generic when the method is called

3.2 definition syntax of generic methods

  • Syntax:
Permission modifier <T,E...> Return value type method name(parameter list ){
	...
}
// Example:
// Indicates a method with return value type E and parameter type E:
public <E> E fun(E e){
    
}

4. Type erase

4.1 concept

Because generics are Java 1 The concept introduced after 5 has no generics before

So why is generic code downward compatible?

The reason is that the generic information only exists in the code compilation stage, and the generic related information will be erased before entering the JVM, that is, the generic information will not enter the runtime stage

4.2 comparison before and after type erasure

  • Unlimited type erasure:
public class A<T> {
	private T key;
	
	public T getKey(){
		return key;
	}
	
	public void setKey(T key){
		this.key = key;
	}
}

// After generic erase:
public class A {
	private Object key;
	
	public Object getKey(){
		return key;
	}
	
	public void setKey(Object key){
		this.key = key;
	}
}
  • Type erasure with restricted types:
public class A<T extends Number> {
	private T key;
	
	public T getKey(){
		return key;
	}
	
	public void setKey(T key){
		this.key = key;
	}
}

// After generic erase:
public class A {
	private Number key;
	
	public Number getKey(){
		return key;
	}
	
	public void setKey(Number key){
		this.key = key;
	}
}
  • Type erasure of generic methods:
public <T extends Number> T getValue(T value){
	return value;
}

// After generic erase:
public Number getValue(Number value){
	return value;
}
  • After the non generic class implements the generic interface, the type erasure of the implementation method uses the bridging method:
public interface Info<T> {
 	T info(T var);   
}

public class InfoImpl implement Info<Integer> {
 	@Override
    public Integer info(Integer var) {
     	return var;   
    }
}

// After type erase:
public interface Info {
 	Object info(Object var);   
}

public class InfoImpl implement Info {
    public Integer info(Integer var) {
     	return var;   
    }
    
    // Bridge method to ensure the implementation relationship between interface and class
 	@Override
    public Object info(Object var) {
     	return info( (Integer)var );   
    }
}

4.3 code verification:

List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();

Class classStrList = strList.getClass();
Class classIntList = intList.getClass();

if(classStrList.equals(classIntList)){
    System.out.println("Generic types are essentially the same");
}

Output results:

Generic types are essentially the same

5. Type wildcard

5.1 introduction of wildcards

We know that Ingeter is a subclass of Number. At the same time, we also verified that generic < Ingeter > and generic < Number > are actually the same basic types in type erasure. So the question is, in the method using generic < Number > as a formal parameter, can you use the argument of generic < Ingeter >? Logically similar to generic < Number > and generic < Ingeter >, can they be regarded as generic types with parent-child relationship?

In fact, this polymorphic formal parameter argument passing method is wrong, because the same generic type can correspond to multiple versions (because the parameter type is uncertain), and different versions of generic class instances are incompatible.

In this regard, Java introduces the generic type wildcard:?, Type wildcards can replace specific type arguments

in other words,? Is a type argument, not a type parameter

Take the ArrayList constructor as an example:

	public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // defend against c.toArray (incorrectly) not returning Object[]
            // (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

We notice that the parameters in the ArrayList constructor are written as <? Extensions E > and E is the generic identifier used by ArrayList to define the class. What does this extension mean? This involves the upper and lower limits of wildcards:

5.2 upper limit of type wildcard

Syntax:

class/Interface<? extends Argument type 1>

This means that the type of the generic type can only be argument type 1 or a subclass of argument type 1, which limits the upper limit of type wildcards

Code example:

public void show(List<? extends Number> list) {
    ...
    // If only?, Without extensions, when defining the variable list, the generic parameter in the list defaults to Object. At this time, the elements in the list become other types and need to be forced
    // However, after using extends, the generic parameter in the list is required to be the upper limit Number by default, and the lower limit can be a subclass of any Number
}

5.3 lower limit of type wildcard

Syntax:

class/Interface<? super Argument type 2>

The type representing the generic type can only be argument type 2 or the parent type of argument type 2

6. Generics and arrays

  1. When creating generic arrays, you can specify array references with generics (that is, before the equal sign), but you cannot directly create array objects with generics

    Because arrays need to know the data type throughout the programming period, while generic types will have type erasure during the compilation period

    However, array references can be passed to generic arrays in the form of anonymous arrays:

ArrayList<String>[] listarr = new ArrayList<>[5]; // report errors
// Passed to generic arrays through anonymous array references:
ArrayList<String>[] listarr = new ArrayList[5];// The second half is an anonymous non generic array
// amount to:
ArrayList[] list = new ArrayList[5];// Notice the second half of the two
ArrayList<String>[] listarr = list; // The above is equivalent to these two sentences
/**
  But to pass an anonymous array reference to a generic array
  You cannot create a normal array and pass it again (to prevent inconsistency because the type is changed)
*/ 

  1. You can use Java lang.reflect. Array newInstance(Class, int) to create T < > array (Array.newInstance)

    (however, in real development, try to use generic collections instead of generic arrays)

    Return valuemethoddescribe
    static ObjectNewinstance (class <? > componenttype (component type, int length)Creates a new array with the specified component type and length
    static ObjectNewinstance (class <? > componenttype, int... dimensions)Creates a new array with the specified component type and size

Note: strong rotation is required because Object is returned!

T[] arr = new T(5); // An error is reported because the T type is not known yet
// Only through array Newinstance to create T < > array
// Example:
public class Fruit<T> {
    private T[] arr; // Cannot initialize directly
    
    // Call array. Through the constructor Newinstance creates a generic array
    public Fruit(Class<T> clz, int len){
        arr = (T[])Array.newInstance(clz, len);
    }
    
    // Fill array into elements
    public void put(int index, T item){
        arr[index] = item;
    }
    
    // Get array elements
    public T get(int index){
        return arr[index];
    }
    
    // Get array
    public T[] getArr(){
        return arr;
    }
}

// Use the class written above
public class Test{
    public static void main(String[] agrs){
        Fruit<String> fruit = new Fruit<>(Stirng.class, 3);
        // Create a String type, String Class pass it in
        // Then use the three methods created above
    }
}

Topics: Java Back-end