为什么需要泛型
泛型利于代码重用。比如实现针对某一种具体数据类型的功能,将具体数据类型替换为泛型,则可以实现为针对多种数据类型的功能,极大的提高了功能的复用性。
类型安全,让编译器帮助我们进行类型检查。指定泛型中的类型,让java编译器帮助我们检查类型以及类型转换,不再需要我们自己进行类型判断及强制装换。
泛型的使用
//动物类
public class Animal {
public void eat(){
System.out.println("动物吃");
}
}
//猫类
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("猫吃东西");
}
public void cry(){
System.out.println("喵喵叫");
}
}
//卡菲猫
public class Garfield extends Cat {
}
//泛型类的使用,这种泛型使用方式会导致类型被擦除为所有类的父类Object
//这意味着你在运行期会丢失原本类型的所有信息,无法使用原本类相关的任何属性和方法
public class GenericClass<T> {
//泛型擦除,查看字节码data的类型为 Ljava/lang/Object; data
private T data;
GenericClass(T data){
this.data = data;
}
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
}
//使用了限制的泛型,通过这种方式可以一定程度上弥补第一种方式的弊端
//因为泛型类型被擦除为指定父类,这样可以使用父类的方法
public class GenericExtendsClass<T extends Animal> {
//泛型擦除为Animal,查看字节码data的类型为 L easycode/Animal; data
private T data;
GenericExtendsClass(T data){
this.data = data;
}
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
public void doOtherSomething(){
//调用了Animal特有的方法
data.eat();
}
}
//泛型方法的使用,只能定义静态泛型方法
private static <T> T genericAdd(T a, T b) {
System.out.println(a + "+" + b + "="+a+b);
return a;
}
java泛型的本质
c++的泛型是根据模板类生成不同的类达到泛型的目的,但是java的泛型并不是如此的,java是通过泛型擦除的方式来实现泛型,所以java的泛型也被称为伪泛型,即只存在于编译期的泛型,在运行阶段找不到泛型信息。
所以,可以将java的泛型当做是一种类型检查的手段,而不要像c++一样将泛型作为实际的类型来使用。
Java泛型中的约束
泛型不能实例化。因为java的泛型擦除,所以在运行期不知道泛型信息,自然也就无法实例化。
类或接口的静态域中不能引用泛型类型变量,但是可以定义静态泛型方法。
基本数据类型无法作为泛型类型。因为泛型擦除其实就是将子类赋值给父类,所以基本数据类型无法作为泛型类型。
无法使用 instanceof 关键字或者 ==运算符 判断泛型类的类型,需要借助别的方法(Type类型),但是对原生数据类型无影响
泛型类的原生数据类型与所传递的泛型无关,无论传递什么类型,原生类都是一样的
-
泛型数组可以声明但无法实例化。原因同泛型不能实例化,但是可以实例化非泛型数组,然后通过定义变量的泛型来进行类型检查。
//实例化泛型数组,编译不通过 //ArrayList<String>[] listArray = new ArrayList<String>[5]; //实例化非泛型数组,编译通过 //通过给listArray添加泛型定义来进行泛型检查 ArrayList<String>[] listArray = new ArrayList[5]; //编译不通过 //listArray[0] = new ArrayList<Integer>(); //编译通过 listArray[0] = new ArrayList<String>();
泛型类不能继承Exception或者Throwable
不能捕获泛型类型限定的异常,只能将泛型类型限定的异常抛出
泛型通配符
泛型通配符主要用于一些参数的接收或返回。
通配符类型
<? extends Parent>:指定了泛型类的上界,一般用于获取元素即get first,因为可以肯定元素为Parent的某一个子类或Patent,但是具体是哪一个类型无法确定,所以无法添加,只能获取。
<? super Child>:指定了泛型的下界,一般用于添加元素即put first,因为可以肯定元素为Child的父类,所以可以添加Child类型或者Child的子类,都是满足多态存储的。
-
<?>:等价于<? extends Object>,即没有限制的泛型类型。
<?>比如 List<?>一般作为参数来接收外部的集合,或者返回一个不知道具体元素类型的集合。因为<?>代表未知类型,所以不允许往里面添加元素,只能取出来。比如public List<?> getList(){ return new ArrayList<String>(); }
//测试 extends 和 super
List<Animal> animal = new ArrayList<Animal>();
ArrayList<Cat> cat = new ArrayList<>();
ArrayList<Garfield> garfield = new ArrayList<>();
animal.add(new Animal());
cat.add(new Cat());
garfield.add(new Garfield());
//测试赋值操作
//编译出错,Animal是Cat的父类,不能通过泛型检测
// List<? extends Cat> extendsCatFromAnimal = animal;
List<? super Cat> superCatFromAnimal = animal;
List<? extends Cat> extendsCatFromCat = cat;
List<? super Cat> superCatFromCat = cat;
List<? extends Cat> extendsCatFromCarfield = garfield;
//编译出错 Garfield是 Cat的子类,不能通过泛型检测
// List<? super Cat> superCatFromCarfield = carfield;
//测试add方法
//编译出错,extends只能获取元素而不能添加元素
// extendsCatFromCat.add(new Animal());
// extendsCatFromCat.add(new Cat());
// extendsCatFromCat.add(new Garfield());
//编译出错,可以添加Cat及其子类,但是不能添加其父类Animal
// superCatFromCat.add(new Animal());
superCatFromCat.add(new Cat());
superCatFromCat.add(new Garfield());
//测试get方法
Object catExtends2 = extendsCatFromCat.get(0);
Cat catExtends1 = extendsCatFromCat.get(0);
// Garfield garfield1 = extendsCatFromCarfield.get(0);
//可以确定Object是所有类的父类
Object catSuper1 = superCatFromCat.get(0);
Object catSuper2 = superCatFromAnimal.get(0);
如何获取泛型的参数类型
获取泛型的类型参数类型需要用到Type接口以及它的子接口。
获取泛型的类型参数类型需要用到Type接口以及它的子接口。
Type类型的子接口有ParameterizedType、GenericArrayType、TypeVariable、WildcardType和Class实现类。
public interface ParameterizedType extends Type {
//private List<String> list;
//获取<>中的数据类型,即String
Type[] getActualTypeArguments();
//获取<>前面的实际类型,即List
Type getRawType();
//如果这个类型是某个类型的所属,获得这个所有者类型,否则返回null
Type getOwnerType();
}
public interface GenericArrayType extends Type {
// T[]
//获取数组元素类型,即 T
Type getGenericComponentType();
}
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {
//private T t;
//获取泛型的上界,可以通过extends通配符指定,默认是Object
Type[] getBounds();
//获取声明该类型变量实体,即该变量所在的类、方法等
D getGenericDeclaration();
//获得名称,即K、V、E
String getName();
}
public interface WildcardType extends Type {
//获取泛型表达式上界
Type[] getUpperBounds();
//获取泛型表达式下界
Type[] getLowerBounds();
}
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
Type接口时是Java编程语言中所有类型的公共高级接口,其中的所有类型如下所示:
原始类型(一般的java类包括枚举、注解、非泛型数组),对应 Class 类
参数化类型(List<String>、Map<String, Object>这种形式),对应 ParameterizedType 接口
数组类型(T[]),对应 GenericArrayType 接口
类型变量(T),对应 TypeVariable 接口
基本类型,对应Class对象
举个例子(获取泛型类型)
public class GenericTest {
static class Example{
//参数化类型
private List<String> list;
}
@Test
public void test() throws Exception{
Class<Example> exampleClass = Example.class;
Field field = exampleClass.getDeclaredField("list");
Type type = field.getGenericType();
Assert.assertTrue(type instanceof ParameterizedType);
ParameterizedType parameterizedType = (ParameterizedType)type;
Assert.assertEquals(String.class ,parameterizedType.getActualTypeArguments()[0]);
Assert.assertEquals(List.class, parameterizedType.getRawType());
}
}
参考如下文章:
Java泛型
Type类型常用API介绍
Type类型Demo