在咱们的理论工作中 泛型(Generics) 是无处不在的,咱们也写过不少,看到的更多,如,源码、开源框架... 随处可见,然而,咱们真正了解泛型吗?了解多少呢?例如:BoxBox<Object>Box<?>Box<T>Box<? extends T>Box<? super T> 之间的区别是什么?本篇文章将会对 泛型(Generics) 进行全面的解析,让咱们对泛型有更深刻的了解。

本篇文章的示例代码放在 Github 上,所有知识点,如图:

Lucy 喜爱吃????(为什么要应用泛型)

首先,通过一个盘子装水果小故事来关上咱们的泛型探索之旅(咱们为什么要应用泛型),故事场景如下:

Lucy 到 James 家做客,James 须要款待客人,且晓得 Lucy 喜爱吃橘子????,于是应用水果盘装满了????来款待客人

这个场景怎么用代码体现呢,咱们来新建几个类,如下:

Fruit:水果类

package entity;public class Fruit {    @Override    public String toString() {        return "This is Fruit";    }}

Apple:苹果类,继承水果类

package entity;public class Apple extends Fruit {    @Override    public String toString() {        return " Apple ????";    }}

Orange:橘子类,继承水果类

package entity;public class Orange extends Fruit {    @Override    public String toString() {        return " Orange ????";    }}

Plate:水果盘接口

package entity;public interface Plate<T> {    public void set(T t);    public T get();}

FruitPlate:水果盘类,实现水果盘接口

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() {        int index = items.size() - 1;        if(index >= 0) return (Fruit) items.get(index);        return null;    }}

AiFruitPlate:智能水果盘,实现水果盘接口

package entity;import java.util.ArrayList;import java.util.List;/** * 应用泛型类定义 * @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() {        int index = fruits.size() - 1;        if(index >= 0) return fruits.get(index);        return null;    }}

Person:人类

package entity;public class Person {}

Lucy:Lucy类,继承 Person 类,她领有吃橘子的能力 eat

import entity.Orange;import entity.Person;public class Lucy extends Person {    public void eat(Orange orange) {        System.out.println("Lucy like eat" + orange);    }}

James:James类,继承 Person 类,他领有获取水果盘的能力 getAiFruitPlate

import entity.*;public class James extends Person {    public FruitPlate getPlate() {        return new FruitPlate();    }    public AiFruitPlate getAiFruitPlate() {        return new AiFruitPlate();    }    public void addFruit(FruitPlate fruitPlate, Fruit fruit) {        fruitPlate.set(fruit);    }    public void add(AiFruitPlate<Orange> aiFruitPlate, Orange orange) {        aiFruitPlate.set(orange);    }}

Scenario:测试类

import entity.*;public class Scenario {    public static void main(String[] args) {        scenario1();        scenario2();    }    //没有应用泛型    private static void scenario1() {        James james = new James();        Lucy lucy = new Lucy();        FruitPlate fruitPlate = james.getPlate(); // James 拿出水果盘        james.addFruit(fruitPlate,new Orange()); // James 往水果盘里装橘子        lucy.eat((Orange) fruitPlate.get()); // 须要转型为 Orange    }    //应用了泛型    private static void scenario2() {        James james = new James();        Lucy lucy = new Lucy();        AiFruitPlate<Orange> aiFruitPlate = james.getAiFruitPlate(); // James 拿出智能水果盘(晓得你须要装橘子)        james.add(aiFruitPlate, new Orange()); // James 往水果盘里装橘子(如果,装的不是橘子会揭示)        lucy.eat(aiFruitPlate.get()); // 不须要转型    }}

运行后果,如下:

Lucy like eat  Orange ????Lucy like eat  Orange ????Process finished with exit code 0

咱们能够很显著的看出,应用了泛型之后,不须要类型转换,如果,咱们把 scenario1() 办法,略微改下,如下:

    private static void scenario1() {        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());    }

编译器不会提醒有问题,然而运行之后报错,如下:

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

而,咱们把 scenario2() (应用了泛型)做出同样的批改,如下:

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

编译器,会提醒咱们有谬误,如图:

通过以上案例,很清晰的晓得咱们为什么要应用泛型,如下:

  • 打消类型转换
  • 在编译时进行更强的类型查看
  • 减少代码的复用性

泛型类(Generic Class)

泛型类是通过类型进行参数化的类,这样说可能不是很好了解,之后咱们用代码演示。

一般类(A Simple Class)

首先,咱们来定义一个一般的类,如下:

package definegeneric;public class SimpleClass {    private Object object;    public Object getObject() {        return object;    }    public void setObject(Object object) {        this.object = object;    }}

它的 getset 办法承受和返回一个 Object,所以,咱们能够随便的传递任何类型。在编译时无奈查看类型的应用,咱们能够传入 Integer 且取出 Integer,也能够传入 String ,从而容易导致运行时谬误。

泛型类(A Generic Class)

泛型类的定义格局如下:

class name<T1,T2,...,Tn>{  ...}

在类名之后的 <> 尖括号,称之为类型参数(类型变量),定义一个泛型类就是应用 <> 给它定义类型参数:T1、T2 ... Tn。

而后,咱们把 SimpleClass 改成泛型类,如下:

package definegeneric;public class GenericClass<T> {    private T t;    public T getT() {        return t;    }    public void setT(T t) {        this.t = t;    }}

所以的 object 都替换成为 T,类型参数能够定义为任何的非根本类型,如:class类型、interface类型、数组类型、甚至是另一个类型参数。

调用和实例化泛型类型(nvoking and Instantiating a Generic Type)

要想应用泛型类,必须执行泛型类调用,如:

GenericClass<String> genericClass;

泛型类的调用相似于办法的调用(传递了一个参数),然而,咱们没有将参数传递给办法,而是,将类型参数(String)传递给了 GenericClass 类自身。

此代码不会创立新的 GenericClass 对象,它只是申明了 genericClass 将保留对 String 的援用

要实例化此类,要应用 new 关键字,如:

GenericClass<String> genericClass = new GenericClass<String>();

或者

GenericClass<String> genericClass = new GenericClass<>();

在 Java SE 7 或更高的版本中,编译器能够从上下文推断出类型参数,因而,能够应用 <> 替换泛型类的构造函数所需的类型参数

类型参数命名标准(Type Parameter Naming Conventions)

咱们的类型参数是否肯定要写成 T 呢,依照标准,类型参数名称是单个大写字母。

罕用的类型参数名称有,如:

类型参数含意
EElement
KKey
NNumber
VValue
S,U,V...2nd, 3rd, 4th type

多类型参数(Multiple Type Parameters)

泛型类能够有多个类型参数,如:

public interface MultipleGeneric<K,V> {    public K getKey();    public V getValue();}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() {        return key;    }    @Override    public V getValue() {        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());    }}

输入后果:

key:per, value:6key:per, value:lsyProcess finished with exit code 0

如上代码,new ImplMultipleGenericK 实例化为 String,将 V 实例化为 Integer ,因而, ImplMultipleGeneric 结构函数参数类型别离为 StringInteger,在编写 new ImplMultipleGeneric 代码时,编辑器会主动填写 <> 的值

因为,Java 编译器会从申明 ImplMultipleGeneric 推断出 KV 的类型,因而咱们能够简写为,如下:

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());

泛型接口(Generic Interface)

定义泛型接口和定义泛型类类似(泛型类的技术可同用于泛型接口),如下:

interface name<T1,T2,...,Tn>{  ...}

咱们来定义一个泛型接口,如下:

package definegeneric;public interface Genertor<T> {    public T next();}

那么,如何实现一个泛型接口呢,咱们应用两种形式来实现泛型接口,如下:

应用泛型类,实现泛型接口,且不指定确切的类型参数,所以,实现的 next() 返回值主动变成 T

package definegeneric.impl;import definegeneric.Genertor;public class ImplGenertor<T> implements Genertor<T> {    @Override    public T next() {        return null;    }}

应用一般类,实现泛型接口,且指定确切的类型参数为 String,所以,实现的 next() 返回值主动变成 String

package definegeneric.impl;import definegeneric.Genertor;public class ImplGenertor2 implements Genertor<String> {    @Override    public String next() {        return null;    }}

泛型办法(Generic Methods)

泛型办法应用了类型参数的办法,泛型办法比拟独立,能够申明在 一般类、泛型类、一般接口、泛型接口中。

泛型办法定义格局,如下:

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

泛型办法的类型参数列表,在 <> 内,该列表必须在办法返回类型之前;对于动态的泛型办法,类型参数必须在 static 之后,办法返回类型之前。

一般类里定义泛型办法(Generic methods in a Simple Class)

咱们在一般类中定义泛型办法,如下:

package methodgeneric;public class MethodGeneric {    //定义一个泛型办法    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"));    }}

methodGeneric.<String>genericMethod("java","dart","kotlin") 通常能够省略掉 <> 的内容,编译器将推断出所需的类型,和调用一般办法一样,如:

methodGeneric.genericMethod("java","dart","kotlin")

泛型类里定义泛型办法(Generic methods in a Generic Class)

咱们在泛型类中定义泛型办法,如下:

package methodgeneric;public class MethodGeneric2 {    static class Fruit{        @Override        public String toString() {            return "fruit";        }    }    static class Apple extends Fruit {        @Override        public String toString() {            return "Apple";        }    }    static class Person{        @Override        public String toString() {            return "person";        }    }    //定义了泛型类    static class ShowClass<T> {        //定义了一般办法        public void show1(T t){            System.out.println(t.toString());        }        //定义了泛型办法        public <E> void show2(E e) {            System.out.println(e.toString());        }        //定义了泛型办法        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);   //能够放入 apple,因为 apple 是 fruit 的子类        showClass.show1(person); //此时,编译器会报错,因为 ShowClass<Fruit> 曾经限定类型        showClass.show2(apple); //能够放入,泛型办法 <E> 能够是任何非根本类型        showClass.show2(person);//能够放入,泛型办法 <E> 能够是任何非根本类型        showClass.show3(apple); //能够放入,泛型办法 <T> 和泛型类中的 <T> 不是同一条 T,能够是任何非根本类型        showClass.show3(person); //能够放入,泛型办法 <T> 和泛型类中的 <T> 不是同一条 T,能够是任何非根本类型    }}

在泛型类中定义泛型办法时,须要留神,泛型类里的泛型参数 <T> 和泛型办法里的泛型参数 <T> 不是同一个。

限定类型参数(Bounded Type Parameters)

咱们常常看到相似 public <U extends Number> void inspect(U u) 的代码,<U extends Number> 就是限度类型参数,只对数字进行操作且只承受 Number 或其子类。

要申明一个限定的类型参数,须要在参数类型后加上 extends 关键字,而后是其下限类型(类或接口)。

限定类型参数的泛型类(Generic Class of Bounded Type Parameters)

泛型类也能够应用限定类型参数,如下:

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<>(); //只能传入实现了 Comparable 接口的类型        boundedClass.setT("iOS");        System.out.println(boundedClass.min("android"));    }}

限定类型参数的泛型办法(Generic methods of Bounded Type Parameters)

泛型办法也能够应用限定类型参数,如下:

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,666));    }}

多重限定(Multiple Bounds)

限定类型参数,也能够为多个限定,如:

<T extends B1 & B2 & B3>

多个限定参数,如果其中有类,类必须放在第一个地位,例如:

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

泛型,继承和子类型(Generics, Inheritance, and Subtypes)

在后面的盘子装水果小故事里咱们曾经创立好了一些水果类,如下:

public class Fruit {    @Override    public String toString() {        return "This is Fruit";    }}public class Apple extends Fruit {    @Override    public String toString() {        return " Apple ????";    }}public class Orange extends Fruit {    @Override    public String toString() {        return " Orange ????";    }}public class QIOrange extends Orange {    @Override    public String toString() {        return "qi Orange ????";    }}

他们的继承关系,如图:

家喻户晓,咱们能够把子类赋值给父类,例如:

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

泛型也是如此,咱们定义一个水果盘子的泛型类,如下:

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() {        int index = fruits.size() - 1;        if(index >= 0) return fruits.get(index);        return null;    }}

所以,是 Fruit 的子类都能够放入水果盘里,如下:

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

当初,James 能够获取盘子,如下:

public class James extends Person {    public FruitPlateGen getAiFruitPlateGen(FruitPlateGen<Fruit> plate) {        return new FruitPlateGen();    }}

如是,James 想获取放橘子的盘子,如下:

James james = new James();james.getAiFruitPlateGen(new FruitPlateGen<Fruit>()); //获取胜利james.getAiFruitPlateGen(new FruitPlateGen<Orange>()); //编译器报错

尽管,OrangeFruit 的子类,然而,FruitPlateGen<Orange> 不是 FruitPlateGen<Fruit> 的子类,所以,不能传递产生继承关系。

泛型类和子类型(Generic Classes and Subtyping)

咱们能够通过继承(extends)或实现(implements)泛型类或接口,例如:

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

此时,ExtendFruitPlate<Orange> 就是 FruitPlateGen<Fruit> 的子类,James 再去拿盘子,就不会有谬误提醒:

james.getAiFruitPlateGen(new ExtendFruitPlate<Orange>());

通配符(Wildcards)

咱们常常看到相似 List<? extends Number> 的代码,? 就是通配符,示意未知类型。

下限通配符(Upper Bounded Wildcards)

咱们能够应用下限通配符来放宽对变量的限度,例如,上文提到的 FruitPlateGen<Fruit>FruitPlateGen<Orange>() 就能够应用下限通配符。

咱们来改写一下 getAiFruitPlateGen 办法,如下:

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

这时候,James 想获取放橘子的盘子,如下:

James james = new James();james.getAiFruitPlateGen2(new FruitPlateGen<Fruit>()); //获取胜利james.getAiFruitPlateGen2(new FruitPlateGen<Orange>()); //获取胜利

下限通配符 FruitPlateGen<? extends Fruit> 匹配 FruitFruit 的任何子类型,所以,咱们能够传入 AppleOrange 都没有问题。

上限通配符(Lower Bounded Wildcards)

下限通配符将未知类型限定为该类型或其子类型,应用 extends 关键字,而上限通配符将未知类型限定为该类型或其父类型,应用 super 关键字。

咱们再来宽展一下 getAiFruitPlateGen 办法,如下:

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

这时候,James 只能获取 FruitPlateGen<Fruit>FruitPlateGen<Apple> 的盘子,如下:

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

上限通配符 FruitPlateGen<? super Apple> 匹配 AppleApple 的任何父类型,所以,咱们能够传入 AppleFruit

通配符和子类型(Wildcards and Subtyping)

在 泛型,继承和子类型 章节有讲到,尽管,OrangeFruit 的子类,然而,FruitPlateGen<Orange> 不是 FruitPlateGen<Fruit> 的子类。然而,你能够应用通配符在泛型类或接口之间创立关系。

咱们再来回顾下 Fruit 的继承关系,如图:

代码,如下:

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

这个代码是没有问题的,FruitApple 的父类,所以,能够把子类赋值给父类。

代码如下:

List<Apple> apples = new ArrayList<>();List<Fruit> fruits = apples; // 编辑器报错

因为,List<Apple> 不是 List<Fruit> 的子类,实际上这两者无关,那么,它们的关系是什么?如图:

List<Apple>List<Fruit> 的公共父级是 List<?>

咱们能够应用上上限通配符,在这些类之间创立关系,如下:

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

下图展现了上上限通配符申明的几个类的关系,如图:

PECS准则(Producer extends Consumer super)

在上文中有 FruitPlateGen 水果盘子的类,咱们尝试应用上上限通配符来实例化水果盘,代码如下:

Apple apple = new Apple();Orange orange = new Orange();Fruit fruit = new Fruit();FruitPlateGen<? extends Fruit> fruitPlateGen = new FruitPlateGen<>();fruitPlateGen.set(apple); // errorfruitPlateGen.set(orange); // errorfruitPlateGen.set(fruit); // errorFruit fruit1 = fruitPlateGen.get(); // OKOrange orange1 = fruitPlateGen.get(); // errorApple apple1 = fruitPlateGen.get(); // error

下限通配符无奈 set 数据,然而,能够 get 数据且只能 get 到其下限 Fruit,所以,下限通配符能够平安的拜访数据。

在来看一下代码,如下:

FruitPlateGen<? super Apple> fruitPlateGen1 = new FruitPlateGen<>();fruitPlateGen1.set(apple); // OKfruitPlateGen1.set(orange); // errorfruitPlateGen1.set(fruit); // errorObject object = fruitPlateGen1.get(); // OKFruit fruit2 = fruitPlateGen1.get(); // errorApple apple2 = fruitPlateGen1.get(); // errorOrange orange2 = fruitPlateGen1.get(); // error

上限通配符能够且只能 set 其上限 Apple,也能够 get 数据,但只能用 Object 接管(因为Object是所有类型的父类,这是一个特例),所以,上限通配符能够平安的写入数据。

所以,在应用上上限通配符时,能够遵循以下准则:

  • 如果你只须要从汇合中取得类型T , 应用<? extends T>通配符
  • 如果你只须要将类型T放到汇合中, 应用<? super T>通配符
  • 如果你既要获取又要搁置元素,则不应用任何通配符

类型擦除(Type Erasure)

Java 语言应用类型擦除机制实现了泛型,类型擦除机制,如下:

  • 编译器会把所有的类型参数替换为其边界(上上限)或 Object,因而,编译出的字节码中只蕴含一般类、接口和办法。
  • 在必要时插入类型转换,已放弃类型平安
  • 生成桥接办法以在扩大泛型类时放弃多态性

泛型类型的擦除(Erasure of Generic Types)

Java 编译器在擦除过程中,会擦除所有类型参数,如果类型参数是有界的,则替换为第一个边界,如果是无界的,则替换为 Object。

咱们定义了一个泛型类,代码如下:

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() { return data; }  ...}

因为类型参数 T 是无界的,因而,Java 编译器将其替换为 Object,如下:

public class Node {  private Object data;  private Node next;  public Node(Object data, Node next) { this.data = data;  this.next = next;}  public Object getData() { return data; }  ...}

咱们再来定义一个有界的泛型类,代码如下:

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() { return data; }  ...}

Java 编译器其替换为第一个边界 Comparable,如下:

public class Node {  private Comparable data;  private Node next;  public Node(Comparable data, Node next) { this.data = data;  this.next = next;}  public Comparable getData() { return data; }  ...}

泛型办法的擦除(Erasure of Generic Methods)

Java 编译器同样会擦除泛型办法中的类型参数,例如:

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

因为 T 是无界的,因而,Java 编译器将其替换为 Object,如下:

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

如下代码:

class Shape {  ...  }class Circle extends Shape {  ...  } class Rectangle extends Shape {  ...  }

有一个泛型办法,如下:

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

Java 编译器将用第一个边界 Shape 替换 T,如下:

public static void draw(Shape shape){  ...}

桥接办法(Bridge Methods)

有时类型擦除会导致无奈意料的状况,如下:

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);  } }

类型擦除后,代码如下:

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);  } }

此时,Node 的办法变为 setData(Object data) 和 MyNode 的 setData(Integer data) 不会笼罩。

为了解决此问题并保留泛型类型的多态性,Java 编译器会生成一个桥接办法,如下:

class MyNode extends Node {  // 生成的桥接办法  public void setData(Object data) {      setData((Integer) data);  }  public void setData(Integer data) {     System.out.println("MyNode.setData");     super.setData(data);  }  ...}

这样 Node 的办法 setData(Object data) 和 MyNode 生成的桥接办法 setData(Object data) 能够实现办法的笼罩。

泛型的限度(Restrictions on Generics)

为了无效的应用泛型,须要思考以下限度:

  • 无奈实例化具备根本类型的泛型类型
  • 无奈创立类型参数的实例
  • 无奈申明类型为类型参数的动态字段
  • 无奈将Casts或instanceof与参数化类型一起应用
  • 无奈创立参数化类型的数组
  • 无奈创立,捕捉或抛出参数化类型的对象
  • 无奈重载每个重载的形式参数类型都擦除为雷同原始类型的办法

无奈实例化具备根本类型的泛型类型

代码如下:

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

创建对象时,不能应用根本类型替换参数类型:

Pair<int, char> p = new Pair<>(8, 'a'); // error

无奈创立类型参数的实例

代码如下:

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

无奈申明类型为类型参数的动态字段

代码如下:

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

类的动态字段是所有非动态对象共享的变量,因而,不容许应用类型参数的动态字段。

无奈将Casts或instanceof与参数化类型一起应用

代码如下:

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

Java 编译器会擦除所有类型参数,所有,无奈验证在运行时应用的参数化类型。

无奈创立参数化类型的数组

代码如下:

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

无奈创立,捕捉或抛出参数化类型的对象

代码如下:

class MathException<T> extends Exception {  ...  } // errorclass QueueFullException<T> extends Throwable{ ... } // error

无奈重载每个重载的形式参数类型都 擦除为雷同原始类型的办法

代码如下:

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

print(Set<String> strSet)print(Set<Integer> intSet) 在类型擦除后是完全相同的类型,所以,无奈重载。

最初,附上本人的博客和GitHub地址:如下

博客地址:https://h.lishaoy.net
GitHub地址:https://github.com/persilee