共计 17064 个字符,预计需要花费 43 分钟才能阅读完成。
在咱们的理论工作中 泛型 (Generics) 是无处不在的,咱们也写过不少,看到的更多,如,源码、开源框架 … 随处可见,然而,咱们真正了解泛型吗?了解多少呢?例如:Box
、Box<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;}
}
它的 get
、set
办法承受和返回一个 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
呢,依照标准,类型参数名称是单个大写字母。
罕用的类型参数名称有,如:
类型参数 | 含意 |
---|---|
E | Element |
K | Key |
N | Number |
V | Value |
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:6
key:per, value:lsy
Process finished with exit code 0
如上代码,new ImplMultipleGeneric
将 K
实例化为 String
,将 V
实例化为 Integer
,因而,ImplMultipleGeneric
结构函数参数类型别离为 String
和 Integer
, 在编写 new ImplMultipleGeneric
代码时,编辑器会主动填写 <>
的值
因为,Java 编译器会从申明 ImplMultipleGeneric
推断出 K
和 V
的类型,因而咱们能够简写为,如下:
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>()); // 编译器报错
尽管,Orange
是 Fruit
的子类,然而,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>
匹配 Fruit
和 Fruit
的任何子类型,所以,咱们能够传入 Apple
、Orange
都没有问题。
上限通配符(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>
匹配 Apple
和 Apple
的任何父类型,所以,咱们能够传入 Apple
、Fruit
。
通配符和子类型(Wildcards and Subtyping)
在 泛型,继承和子类型 章节有讲到,尽管,Orange
是 Fruit
的子类,然而,FruitPlateGen<Orange>
不是 FruitPlateGen<Fruit>
的子类。然而,你能够应用通配符在泛型类或接口之间创立关系。
咱们再来回顾下 Fruit
的继承关系,如图:
代码,如下:
Apple apple = new Apple();
Fruit fruit = apple;
这个代码是没有问题的,Fruit
是 Apple
的父类,所以,能够把子类赋值给父类。
代码如下:
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; // OK
List<? 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); // error
fruitPlateGen.set(orange); // error
fruitPlateGen.set(fruit); // error
Fruit fruit1 = fruitPlateGen.get(); // OK
Orange orange1 = fruitPlateGen.get(); // error
Apple apple1 = fruitPlateGen.get(); // error
下限通配符无奈 set
数据,然而,能够 get
数据且只能 get
到其下限 Fruit
,所以,下限通配符能够平安的拜访数据。
在来看一下代码,如下:
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
上限通配符能够且只能 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 {...} // error
class 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