Generics are ubiquitous in our practical work, we have written quite a bit and seen more, such as source code, open source frameworks… It’s everywhere, but do we really understand generics? How much do you understand? For example, Box, Box, Box
, Box

, Box
, Box
? This article will take a comprehensive look at Generics to give us a deeper understanding of Generics.

The sample code for this article is posted on Github.

Lucy likes to eat 🍊 (why use generics)

First, open our exploration of generics (why we use generics) with a fruit on a plate story set in the following scenario:

James needs to entertain guests. Knowing that Lucy likes to eat oranges 🍊, he fills a fruit bowl with 🍊 to entertain guests

To represent this scenario in code, let’s create a few new classes as follows:

Neil: Fruit

package entity;

public class Fruit {

    @Override
    public String toString(a) {

        return "This is Fruit"; }}Copy the code

Apple: Apple, inherited from fruit

package entity;

public class Apple extends Fruit {

    @Override
    public String toString(a) {

        return "Apple 🍎"; }}Copy the code

Orange: The fruit of the Orange family

package entity;

public class Orange extends Fruit {

    @Override
    public String toString(a) {

        return "Orange 🍊"; }}Copy the code

With the Plate on the Plate

package entity;

public interface Plate<T> {

    public void set(T t);

    public T get(a);

}
Copy the code

FruitPlate: FruitPlate class, to achieve FruitPlate interface

package entity;

import java.util.ArrayList;
import java.util.List;

public class FruitPlate implements Plate {

    private List items = new ArrayList(6);

    @Override
    public void set(Object o) {
        items.add(o);
    }

    @Override
    public Fruit get(a) {
        int index = items.size() - 1;
        if(index >= 0) return (Fruit) items.get(index);
        return null; }}Copy the code

AiFruitPlate: Intelligent fruit plate, realize fruit plate interface

package entity;

import java.util.ArrayList;
import java.util.List;
/** * use generic class definition *@param <T>
 */
public class AiFruitPlate<T> implements Plate<T> {

    private List<T> fruits = new ArrayList<T>(6);
    @Override
    public void set(T t) {
        fruits.add(t);
    }

    @Override
    public T get(a) {
        int index = fruits.size() - 1;
        if(index >= 0) return fruits.get(index);
        return null; }}Copy the code

Person:

package entity;

public class Person {}Copy the code

Lucy: Lucy class, inherited from Person class, she has the ability to eat oranges eat

import entity.Orange;
import entity.Person;

public class Lucy extends Person {

    public void eat(Orange orange) {

        System.out.println("Lucy like eat"+ orange); }}Copy the code

James: The James class, inherited from the Person class, has the ability to get the getAiFruitPlate

import entity.*;

public class James extends Person {

    public FruitPlate getPlate(a) {
        return new FruitPlate();
    }

    public AiFruitPlate getAiFruitPlate(a) {
        return new AiFruitPlate();
    }

    public void addFruit(FruitPlate fruitPlate, Fruit fruit) {
        fruitPlate.set(fruit);
    }

    public void add(AiFruitPlate<Orange> aiFruitPlate, Orange orange) { aiFruitPlate.set(orange); }}Copy the code

Scenario: Test class

import entity.*;

public class Scenario {

    public static void main(String[] args) {
        scenario1();
        scenario2();
    }
    // Generics are not used
    private static void scenario1(a) {
        James james = new James();
        Lucy lucy = new Lucy();
        FruitPlate fruitPlate = james.getPlate(); // James takes out the fruit bowl
        james.addFruit(fruitPlate,new Orange()); // James filled the fruit bowl with oranges
        lucy.eat((Orange) fruitPlate.get()); // Need to transition to Orange
    }
    // Generics are used
    private static void scenario2(a) {
        James james = new James();
        Lucy lucy = new Lucy();
        AiFruitPlate<Orange> aiFruitPlate = james.getAiFruitPlate(); // James takes out the smart fruit bowl (knowing you need oranges)
        james.add(aiFruitPlate, new Orange()); // James fills the fruit bowl with oranges (if it is not an orange, it will be warned)
        lucy.eat(aiFruitPlate.get()); // No transition required}}Copy the code

The result is as follows:

Lucy like eat  Orange 🍊
Lucy like eat  Orange 🍊

Process finished with exit code 0
Copy the code

It becomes clear that a type conversion is not required after the use of generics if we change the scenario1() method a bit, as follows:

    private static void scenario1(a) {
        James james = new James();
        Lucy lucy = new Lucy();
        FruitPlate fruitPlate = james.getPlate();
        james.addFruit(fruitPlate,new Apple()); //new Orange() 改成 new Apple()
        lucy.eat((Orange) fruitPlate.get());
    }
Copy the code

The compiler does not say that there is a problem, but after running an error, as follows:

Exception in thread "main" java.lang.ClassCastException: entity.Apple cannot be cast to entity.Orange
	at Scenario.scenario1(Scenario.java:21)
	at Scenario.main(Scenario.java:7)

Process finished with exit code 1
Copy the code

Instead, we scenario2() (using generics) make the same modification, as follows:

    private static void scenario2(a) {
        James james = new James();
        Lucy lucy = new Lucy();
        AiFruitPlate<Orange> aiFruitPlate = james.getAiFruitPlate();
        james.add(aiFruitPlate, new Apple());
        lucy.eat(aiFruitPlate.get());
    }
Copy the code

The compiler will tell us that there is an error, as shown in the following figure:

error

From the above examples, it is clear why we use generics, as follows:

  • Eliminating type conversions
  • Stronger type checking at compile time
  • Increase code reuse

Generic Classes

Generic classes are classes that are parameterized by type, which may not be easy to understand, but we’ll show it in code later.

A Simple Class

First, let’s define a generic class as follows:

package definegeneric;

public class SimpleClass {

    private Object object;

    public Object getObject(a) {
        return object;
    }

    public void setObject(Object object) {
        this.object = object; }}Copy the code

Its get and set methods accept and return an Object, so we can pass any type at will. There is no way to check the use of a type at compile time, so we can pass in an Integer and fetch it, or we can pass in a String, which is prone to runtime errors.

A Generic Class

Generic classes are defined in the following format:

class name<T1.T2. .Tn>{... }Copy the code

The <> Angle brackets after the class name are called type parameters (type variables). To define a generic class, use <> to give it type parameters: T1, T2… Tn.

We then change SimpleClass to a generic class as follows:

package definegeneric;

public class GenericClass<T> {

    private T t;

    public T getT(a) {
        return t;
    }

    public void setT(T t) {
        this.t = t; }}Copy the code

All objects are replaced with T. Type parameters can be defined as any non-basic type, such as class type, interface type, array type, or even another type parameter.

Nvoking and Instantiating a Generic Type

To use a generic class, you must make a generic class call, such as:

GenericClass<String> genericClass;
Copy the code

The invocation of a GenericClass is similar to the invocation of a method (passing a parameter), but instead of passing the parameter to the method, we pass the type parameter (String) to the GenericClass class itself.

This code does not create a new GenericClass object; it simply declares that GenericClass will hold a reference to String

To instantiate this class, use the new keyword, such as:

GenericClass<String> genericClass = new GenericClass<String>();
Copy the code

or

GenericClass<String> genericClass = new GenericClass<>();
Copy the code

In Java SE 7 and later, the compiler can infer type parameters from the context, so you can use <> to replace type parameters required by the constructor of a generic class

Type Parameter Naming Conventions

Should our type parameter be written as T? According to the specification, the type parameter name is a single uppercase letter.

Common type parameter names are:

Type parameters meaning
E Element
K Key
N Number
V Value
S,U,V… 2nd, 3rd, 4th type

Multiple Type Parameters

Generic classes can have multiple type parameters, such as:

public interface MultipleGeneric<K.V> {
    public K getKey(a);
    public V getValue(a);
}

public class ImplMultipleGeneric<K.V> implements MultipleGeneric<K.V> {

    private K key;
    private V value;

    public ImplMultipleGeneric(K key, V value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public K getKey(a) {
        return key;
    }

    @Override
    public V getValue(a) {
        return value;
    }

    public static void main(String[] args) {
        MultipleGeneric<String, Integer> m1 = new ImplMultipleGeneric<String, Integer>("per".6);
        System.out.println("key:" + m1.getKey() + ", value:" + m1.getValue());

        MultipleGeneric<String,String> m2 = new ImplMultipleGeneric<String, String>("per"."lsy");
        System.out.println("key:" + m2.getKey() + ", value:"+ m2.getValue()); }}Copy the code

Output result:

key:per, value:6
key:per, value:lsy

Process finished with exit code 0
Copy the code

As shown above, new ImplMultipleGeneric instantiates K to String and V to Integer, so The ImplMultipleGeneric constructor arguments are of type String and Integer, respectively, and the editor fills in the value <> automatically when writing the new ImplMultipleGeneric code

Since the Java compiler will infer the types of K and V from declaring ImplMultipleGeneric, we can abbreviate them as follows:

MultipleGeneric<String, Integer> m1 = new ImplMultipleGeneric<>("per".6);
System.out.println("key:" + m1.getKey() + ", value:" + m1.getValue());

MultipleGeneric<String,String> m2 = new ImplMultipleGeneric<>("per"."lsy");
System.out.println("key:" + m2.getKey() + ", value:" + m2.getValue());
Copy the code

Generic Interfaces

Defining a generic interface is similar to defining a generic class (the same techniques apply to generic interfaces), as follows:

interface name<T1.T2. .Tn>{... }Copy the code

Let’s define a generic interface as follows:

package definegeneric;

public interface Genertor<T> {
    public T next(a);
}
Copy the code

So, how to implement a generic interface, we use two ways to implement a generic interface, as follows:

With generic classes, the generic interface is implemented without specifying an exact type parameter, so the return value of next() is automatically changed to T

package definegeneric.impl;

import definegeneric.Genertor;

public class ImplGenertor<T> implements Genertor<T> {

    @Override
    public T next(a) {
        return null; }}Copy the code

Implement the generic interface using ordinary classes and specify the exact type argument as String, so the return value of the implementation next() automatically becomes String

package definegeneric.impl;

import definegeneric.Genertor;

public class ImplGenertor2 implements Genertor<String> {

    @Override
    public String next(a) {
        return null; }}Copy the code

Generic Methods

Generic methods use methods of type parameters. Generic methods are independent and can be declared in normal classes, generic classes, generic interfaces, and generic interfaces.

Generic methods define the format as follows:

public <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2)
Copy the code

A list of type parameters for a generic method, in <>, that must precede the method return type; For static generic methods, the type parameter must come after static and before the method return type.

Define Generic methods in a Simple Class

We define generic methods in ordinary classes as follows:

package methodgeneric;

public class MethodGeneric {

    // Define a generic method
    public <T> T genericMethod(T... t) {
        return t[t.length/2];
    }

    public static void main(String[] args) {
        MethodGeneric methodGeneric = new MethodGeneric();
        System.out.println(methodGeneric.<String>genericMethod("java"."dart"."kotlin")); }}Copy the code

MethodGeneric.

genericMethod(” Java “,”dart”,”kotlin”) can usually omit the contents of <>, and the compiler will infer the desired type, just as it would with a normal method call, as in:

methodGeneric.genericMethod("java"."dart"."kotlin")
Copy the code

Define Generic methods in a Generic Class

We define generic methods in a generic class as follows:

package methodgeneric;

public class MethodGeneric2 {

    static class Fruit{

        @Override
        public String toString(a) {
            return "fruit"; }}static class Apple extends Fruit {

        @Override
        public String toString(a) {
            return "Apple"; }}static class Person{

        @Override
        public String toString(a) {
            return "person"; }}// Generic classes are defined
    static class ShowClass<T> {
        // Common methods are defined
        public void show1(T t){
            System.out.println(t.toString());
        }
        // Generic methods are defined
        public <E> void show2(E e) {
            System.out.println(e.toString());
        }
        // Generic methods are defined
        public <T> void show3(T t) { System.out.println(t.toString()); }}public static void main(String[] args) {

        Apple apple = new Apple();
        Person person = new Person();

        ShowClass<Fruit> showClass = new ShowClass<>();
        showClass.show1(apple);   // We can put apple because apple is a subclass of fruit
        showClass.show1(person); // At this point, the compiler will report an error because ShowClass
      
        has qualified the type
      

        showClass.show2(apple); // The generic method 
      
        can be any non-basic type
      
        showClass.show2(person);// The generic method 
      
        can be any non-basic type
      

        showClass.show3(apple); // The generic method 
      
        is not the same T as the 
       
         in the generic class, and can be any non-basic type
       
      
        showClass.show3(person); // The generic method 
      
        is not the same T as the 
       
         in the generic class, and can be any non-basic type
       
      }}Copy the code

When defining a generic method in a generic class, it is important to note that the generic parameter

in the generic class is not the same as the generic parameter

in the generic method.

Bounded Type Parameters

We often see code like public void inspect(U U). limits type parameters to operate only on numbers and only accept Number or a subclass of it.

To declare a qualified type parameter, you add the extends keyword to the parameter type, followed by its upper type (class or interface).

A Generic Class of Bounded Type Parameters

Generic classes can also use qualified type parameters as follows:

package boundedgeneric;

public class BoundedClass<T extends Comparable> {

    private T t;

    public void setT(T t) {
        this.t = t;
    }

    public T min(T outter){
        if(this.t.compareTo(outter) > 0)
            return outter;
        else
            return this.t;
    }

    public static void main(String[] args) {
        BoundedClass<String> boundedClass = new BoundedClass<>(); Only types that implement the Comparable interface can be passed in
        boundedClass.setT("iOS");
        System.out.println(boundedClass.min("android")); }}Copy the code

Generic Methods of Bounded Type Parameters

Generic methods can also use qualified type parameters as follows:

package boundedgeneric;

public class BoundedGeneric {

    public static <T extends Comparable> T min(T a, T b) {
        if (a.compareTo(b) < 0)
            return a;
        else
            return b;
    }

    public static void main(String[] args) {
        System.out.println(BoundedGeneric.min(66.Awesome!)); }}Copy the code

Multiple Bounds

Qualifies type parameters, or more than one, for example:

<T extends B1 & B2 & B3>
Copy the code

Multiple qualified arguments. If there is a class, the class must be in the first place, for example:

interface A {... }interface B {... }class C {... }class D <T extends C & A & B>
Copy the code

Generics, Inheritance, and Subtypes

We have created some fruit classes in the previous fruit on a plate story, as follows:

public class Fruit {
    @Override
    public String toString(a) {
        return "This is Fruit"; }}public class Apple extends Fruit {
    @Override
    public String toString(a) {
        return "Apple 🍎"; }}public class Orange extends Fruit {
    @Override
    public String toString(a) {
        return "Orange 🍊"; }}public class QIOrange extends Orange {
    @Override
    public String toString(a) {
        return "qi Orange 🍊"; }}Copy the code

Their inheritance relationship, as shown in the figure:

As we all know, we can assign a subclass to a parent class, for example:

Apple apple = new Apple();
Fruit fruit = new Fruit();
fruit = apple;
Copy the code

The same goes for generics. We define a fruit plate generic class as follows:

public class FruitPlateGen<Fruit> implements Plate<Fruit> {

    private List<Fruit> fruits = new ArrayList<>(6);

    @Override
    public void set(Fruit fruit) {
        fruits.add(fruit);
    }

    @Override
    public Fruit get(a) {
        int index = fruits.size() - 1;
        if(index >= 0) return fruits.get(index);
        return null; }}Copy the code

So, any subclass of Fruit can go into a Fruit bowl, like this:

FruitPlateGen<Fruit> fruitPlate = new FruitPlateGen<Fruit>();
fruitPlate.set(new Apple());
fruitPlate.set(new Orange());
Copy the code

Now, James can get the plate as follows:

public class James extends Person {
    public FruitPlateGen getAiFruitPlateGen(FruitPlateGen<Fruit> plate) {
        return newFruitPlateGen(); }}Copy the code

So, James wants to get the orange plate, as follows:

James james = new James();
james.getAiFruitPlateGen(new FruitPlateGen<Fruit>()); // Succeeded
james.getAiFruitPlateGen(new FruitPlateGen<Orange>()); // The compiler reported an error
Copy the code

FruitPlateGen

is not a FruitPlateGen

subclass, so it cannot be passed to produce inheritance relationship.

Generic Classes and Subtyping

We can extend or implement a generic class or interface, for example:

private static class ExtendFruitPlate<Orange> extends FruitPlateGen<Fruit> {}Copy the code

FruitPlateGen


james.getAiFruitPlateGen(new ExtendFruitPlate<Orange>());
Copy the code

Wildcards (Wildcards)

We often see things like List
,? It’s a wildcard. It’s an unknown type.

Upper Bounded Wildcards

FruitPlateGen

and FruitPlateGen

() can be used to limit variables.

Let’s rewrite the getAiFruitPlateGen method as follows:

public FruitPlateGen getAiFruitPlateGen2(FruitPlateGen<? extends Fruit> plate) {
    return new FruitPlateGen();
}
Copy the code

At this point, James wants to get the orange plate, as follows:

James james = new James();
james.getAiFruitPlateGen2(new FruitPlateGen<Fruit>()); // Succeeded
james.getAiFruitPlateGen2(new FruitPlateGen<Orange>()); // Succeeded
Copy the code

FruitPlateGen
matches any subtype of Fruit and Fruit, so we can pass in Apple and Orange without a problem.

Lower Bounded Wildcards

Upper wildcards limit an unknown type to that type or its subtypes using the extends keyword, while lower wildcards limit an unknown type to that type or its parent using the super keyword.

Let’s extend the getAiFruitPlateGen method as follows:

public FruitPlateGen getAiFruitPlateGen3(FruitPlateGen<? super Apple> plate) {
    return new FruitPlateGen();
}
Copy the code

FruitPlateGen

and FruitPlateGen

James james = new James();
james.getAiFruitPlateGen3(new FruitPlateGen<Apple>());
james.getAiFruitPlateGen3(new FruitPlateGen<Fruit>());
Copy the code

The lower limit wildcard is FruitPlateGen
matches any parent of Apple and Apple, so we can pass in Apple, Fruit.

Wildcards and Subtypes (Wildcards and Subtyping)

FruitPlateGen

is not a Fruit

subclass. However, you can use wildcards to create relationships between generic classes or interfaces.

Let’s review the inheritance relationship of Fruit again, as shown in the figure:

The code is as follows:

Apple apple = new Apple();
Fruit fruit = apple;
Copy the code

This code is fine, Fruit is the parent of Apple, so you can assign a subclass to the parent.

The code is as follows:

List<Apple> apples = new ArrayList<>();
List<Fruit> fruits = apples; // The editor reported an error
Copy the code

Because List

is not a subclass of List

, so they’re actually unrelated, so what’s the relationship? As shown in figure:

The common parent of List

and List

is List
.

We can use upper and lower wildcards to create relationships between these classes as follows:

List<Apple> apples = new ArrayList<>();
List<? extends Fruit> fruits1 = apples; // OK
List<? super Apple> fruits2 = apples; // OK
Copy the code

The following diagram shows the relationship of several classes with upper and lower wildcard declarations, as shown below:

PECS principles (Producer extends Consumer Super)

FruitPlateGen = FruitPlateGen = FruitPlateGen = FruitPlateGen = FruitPlateGen

Apple apple = new Apple();
Orange orange = new Orange();
Fruit fruit = new Fruit();

FruitPlateGen<? extends Fruit> fruitPlateGen = new FruitPlateGen<>();
fruitPlateGen.set(apple); // error
fruitPlateGen.set(orange); // error
fruitPlateGen.set(fruit); // error
Fruit fruit1 = fruitPlateGen.get(); // OK
Orange orange1 = fruitPlateGen.get(); // error
Apple apple1 = fruitPlateGen.get(); // error
Copy the code

An upper wildcard cannot set data, but it can get data and only get its upper Fruit, so it can safely access data.

Take a look at the code as follows:

FruitPlateGen<? super Apple> fruitPlateGen1 = new FruitPlateGen<>();
fruitPlateGen1.set(apple); // OK
fruitPlateGen1.set(orange); // error
fruitPlateGen1.set(fruit); // error
Object object = fruitPlateGen1.get(); // OK
Fruit fruit2 = fruitPlateGen1.get(); // error
Apple apple2 = fruitPlateGen1.get(); // error
Orange orange2 = fruitPlateGen1.get(); // error
Copy the code

Lower bound wildcards can and can only set their lower bound Apple, and can also get data, but can only be received with Object (a special case because Object is the parent of all types). Therefore, lower bound wildcards can safely write data.

Therefore, when using upper and lower wildcards, you can follow the following guidelines:

  • If you only need to get type T from the collection, use
    wildcard
  • If you only need to put type T into the collection, use
    wildcard
  • If you want to both get and place elements, do not use any wildcards

Type Erasure

The Java language implements generics using type erasures, which are as follows:

  • The compiler replaces all type arguments with their boundaries (upper and lower limits) or objects, so the compiled bytecode contains only ordinary classes, interfaces, and methods.
  • Type safety is preserved by inserting type conversions when necessary
  • Bridge methods are generated to preserve polymorphism as generic classes are extended

Erasure of Generic Types

The Java compiler erases all type arguments during erasing, replacing them with the first boundary if they are bounded or Object if they are unbounded.

We define a generic class as follows:

public class Node<T> {
  private T data;
  private Node<T> next;
  public Node(T data, Node<T> next) { this.data = data;
  this.next = next;
}
  public T getData(a) { returndata; }... }Copy the code

Since the type parameter T is unbounded, the Java compiler replaces it with Object, as follows:

public class Node {
  private Object data;
  private Node next;
  public Node(Object data, Node next) { this.data = data;
  this.next = next;
}
  public Object getData(a) { returndata; }... }Copy the code

Let’s define a bounded generic class as follows:

public class Node<T extends Comparable<T>> {
  private T data;
  private Node<T> next;
  public Node(T data, Node<T> next) { this.data = data;
  this.next = next;
}
  public T getData(a) { returndata; }... }Copy the code

The Java compiler replaces the first boundary Comparable with the following:

public class Node {
  private Comparable data;
  private Node next;
  public Node(Comparable data, Node next) { this.data = data;
  this.next = next;
}
  public Comparable getData(a) { returndata; }... }Copy the code

Erasure of Generic Methods

The Java compiler also erases type parameters in generic methods, such as:

public static <T> int count(T[] anArray, T elem) {
  int cnt = 0;
  for (T e : anArray)
}
Copy the code

Since T is unbounded, the Java compiler replaces it with Object, as follows:

public static int count(Object[] anArray, Object elem) {
  int cnt = 0;
  for (Object e : anArray) if (e.equals(elem))
}
Copy the code

The following code:

class Shape {... }class Circle extends Shape {... }class Rectangle extends Shape {... }Copy the code

There is a generic method like this:

public static<T extends Shape> void draw(T shape){... }Copy the code

The Java compiler replaces T with the first boundary, Shape, as follows:

public static void draw(Shape shape){... }Copy the code

Bridge Methods

Sometimes type erasure can lead to unexpected situations like this:

public class Node<T> {
  public T data;
  public Node(T data) { this.data = data; }
  public void setData(T data) { 
    System.out.println("Node.setData"); 
    this.data = data; }}public class MyNode extends Node<Integer> {
  public MyNode(Integer data) { super(data); }
  public void setData(Integer data) { 
    System.out.println("MyNode.setData"); 
    super.setData(data); }}Copy the code

After the type is erased, the code looks like this:

public class Node {
  public Object data;
  public Node(Object data) { this.data = data; }
  public void setData(Object data) { 
    System.out.println("Node.setData"); 
    this.data = data; }}public class MyNode extends Node {
  public MyNode(Integer data) { super(data); }
  public void setData(Integer data) { 
    System.out.println("MyNode.setData");
    super.setData(data); }}Copy the code

At this point, Node’s method changes to setData(Object data) and MyNode’s setData(Integer data) will not be overridden.

To solve this problem and preserve the polymorphism of generic types, the Java compiler generates a bridge method like this:

class MyNode extends Node {
  // The generated bridge method
  public void setData(Object data) {
      setData((Integer) data);
  }
  public void setData(Integer data) { 
    System.out.println("MyNode.setData"); 
    super.setData(data); }... }Copy the code

In this way, the Node method setData(Object data) and the bridge method setData(Object data) generated by MyNode can complete the method coverage.

Restrictions on Generics

To use generics effectively, consider the following limitations:

  • A generic type with a primitive type cannot be instantiated
  • Unable to create an instance of a type parameter
  • A static field of type parameter cannot be declared
  • You cannot use Casts or Instanceof with parameterized types
  • Unable to create an array of parameterized types
  • Cannot create, catch, or throw an object of parameterized type
  • Cannot overload a method that erases every overloaded formal parameter type to the same primitive type

A generic type with a primitive type cannot be instantiated

The code is as follows:

class Pair<K.V> {
  private K key;
  private V value;
  public Pair(K key, V value) { 
    this.key = key;
    this.value = value; }... }Copy the code

When creating an object, you cannot replace parameter types with primitive types:

Pair<int.char> p = new Pair<>(8.'a'); // error
Copy the code

Unable to create an instance of a type parameter

The code is as follows:

public static <E> void append(List<E> list) {
   E elem = new E(); // error 
   list.add(elem);
}
Copy the code

A static field of type parameter cannot be declared

The code is as follows:

public class MobileDevice<T> {
  private static T os; // error. }Copy the code

The static fields of a class are variables shared by all non-static objects; therefore, static fields of type parameters are not allowed.

You cannot use Casts or Instanceof with parameterized types

The code is as follows:

public static <E> void rtti(List<E> list) {
  if (list instanceof ArrayList<Integer>) { // error. }}Copy the code

The Java compiler erases all type arguments, so it cannot validate parameterized types used at run time.

Unable to create an array of parameterized types

The code is as follows:

List<Integer>[] arrayOfLists = new List<Integer>[2]; // error
Copy the code

Cannot create, catch, or throw an object of parameterized type

The code is as follows:

class MathException<T> extends Exception {... }// error
class QueueFullException<T> extends Throwable{... }// error
Copy the code

Cannot overload a method that erases every overloaded formal parameter type to the same primitive type

The code is as follows:

public class Example {
  public void print(Set<String> strSet) {}public void print(Set<Integer> intSet) {}}Copy the code

Print (Set

strSet) and print(Set

intSet) are exactly the same type after being erased, so they cannot be overloaded.

Finally, attach my blog and GitHub address:

Blog address: h.lishaoy.net GitHub address: github.com/persilee