Generics in Java

  • 2020-04-01 03:48:56
  • OfStack

Generics: allow you to define a class, interface, or type parameter that will be determined when you declare a variable or create an object.

Generic class or interface

Diamond syntax


//Define < br / >  
public interface List<E> extends Collection<E> 
 
public class HashMap<K,V> extends AbstractMap<K,V>  implements Map<K,V>, Cloneable, Serializable
//Use the < br / >  
List<String> list = new ArrayList();
 
//After Java7, you can omit the type parameter
for the Angle brackets  
List<String> list = new ArrayList<>();

Subclass derived from a generic class


//Approach 1 < br / >  
public class App extends GenericType<String>
 
//Way 2 < br / >  
public class App<T> extends GenericType<T>
 
//Methods 3 < br / >  
public class App extends GenericType

Pseudo generic

There are no true generic classes, and generic classes are transparent to the Java virtual machine.
- the following are all wrong


private static T data;
 
static{
 
    T f;
 
}
 
public static void func(){
 
    T name = 1;
 
}

The following example can verify from the side that there is no generic class

public static void main(String[] args){
 
        List<String> a1 = new ArrayList<>();
        List<Integer> a2 = new ArrayList<>(); 
    System.out.println(a1.getClass() == a2.getClass());
 
    System.out.println(a1.getClass());
 
    System.out.println(a2.getClass());
 
}

The output

true
 
class java.util.ArrayList
 
class java.util.ArrayList

Type wildcards

First of all, let's make it clear that Foo is the parent of Bar, but List< Foo> Not List< Bar> To represent the parent classes of various generics,Java USES "?" To represent generic generics, i.e. List< The & # 63; > List generics with this wildcard cannot set (set) elements, only get (get) elements. Because the program cannot determine the type in the List, it cannot add an object. But the Object you get is definitely of type Object.

The following methods will compile incorrectly:


List<?> list = new ArrayList<>();
 
list.add(new Object());

Some ideas:

1. List< String> Object cannot be treated as List< Object> Object usage, that is, List< String> Class is not List< Object> Subclass of class.

2. Arrays are different from generics: if Foo is a subtype (subclass or subinterface) of Bar, then Foo[] is still a subtype of Bar[]. But G< Foo> Not G< Bar> Is a subtype of.

3. To represent the parent of the various generic lists, we need to use the type wildcard, which is a question mark (?) , pass a question mark as a type argument to the List set as: List< The & # 63; > List of elements of unknown type. This question mark (?) Known as a wildcard, its element types can match any type.

The upper limit of a wildcard

List< The & # 63; Extends SuperType> Represents the parent class or itself of all SuperType generic lists. Generics with wildcard caps cannot have set methods, only get methods.

Setting the wildcard limit can solve the following problem: Dog is an Animal subclass, and there is a getSize method to get the number of lists passed in, the code is as follows


abstract class Animal {
    public abstract void run();
}
class Dog extends Animal {
    public void run() {
        System.out.println("Dog run");
    }
}
public class App {
    public static void getSize(List<Animal> list) {
        System.out.println(list.size());
    }
    public static void main(String[] args) {
        List<Dog> list = new ArrayList<>();
        getSize(list); //Here compile error
    }
}

The reason for the programming error here is List< Animal> Not List< Dog> The parent class. Solution 1 can be the getSize method form parameter List< Animal> List< instead; The & # 63; > , but then you have to cast it every time you get an object. This problem is best solved by using the wildcard upper bound, which can be used to List< Animal> List< instead; The & # 63; Extends Animal> , the compilation will not be wrong, and there is no need to cast.

The lower limit of a wildcard

List< The & # 63; Super SubType> Represents the lower limit of the SubType generic List. Generics with wildcard caps cannot have a get method, only a set method.

Generic method

If you define a class or interface without type parameters, but want to define your own type parameters when defining a method, this is ok. JDK1.5 also provides support for generic methods. The method signature of generic methods has more type parameter declarations than the method signature of ordinary methods. The type parameter declarations are enclosed in Angle brackets, separated by commas (,) between multiple type parameters, and all type parameter declarations are placed between method modifiers and method return value types. The syntax is as follows:


The modifier Return value type Method name (list of classes) {
 
//Method body
 
}

Generic methods allow type parameters to be used to represent type dependencies between one or more parameters of a method, or between method return values and parameters. Generic methods should not be used without such type dependencies. The Collections copy method USES the generic method:

 public static <T> void copy(List<? super T> dest, List<? extends T> src){ ...}

This method requires that the SRC type be either a subclass of the dest type or itself.

Erase and transform

In strict generic code, classes with generic declarations should always take type parameters. But in keeping with the old Java code, it is also possible to use classes with generic declarations without specifying type parameters. If no type parameter is specified for the generic class, the type parameter is called a raw type (the original type), and the default is the first upper bound type specified when the parameter is declared.

When an object with generic information is assigned to a variable with no generic information, all type information between Angle brackets is thrown away. For example, a List< String> When the type is converted to List, the List type check for the collection element becomes the upper bound of the type variable (that is, Object), in which case it is erased.

The sample


class Apple<T extends Number>
 
{
 
 T size;
 
 public Apple()
 
 {
 
 }
 
 public Apple(T size)
 
 {
 
  this.size = size;
 
 }
 
 public void setSize(T size)
 
 {
 
  this.size = size;
 
 }
 
 public T getSize()
 
 {
 
  return this.size;
 
 }
 
}
 
public class ErasureTest
 
{
 
 public static void main(String[] args)
 
 {
 
  Apple<Integer> a = new Apple<>(6);    // 1.
 
  //The getSize method of a returns the Integer object
 
  Integer as = a.getSize();
 
  //Assign the a object to the Apple variable, missing the type information in the Angle brackets
 
  Apple b = a;      // 2.
 
  //B only knows that the type of size is Number
 
  Number size1 = b.getSize();
 
  //The following code causes a compilation error
 
  Integer size2 = b.getSize();  // 3.
 
 }
 
}


Related articles: