ps.原来的标题
- 为什么我们需要泛型?
- 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析);
- 如何限定类型变量?
- 泛型使用中的约束和局限性;
- 泛型类型能继承吗?
- 泛型中通配符类型;
- 虚拟机是如何实现泛型的?
为什么需要泛型
- 泛型扩展了类、接口、方法的适用范围
- 提前生命可使用的类型,在编译期提供提醒
- 避免List使用时的强制类型转换
如何使用泛型
- 泛型类
在类名后加上例如“<K>”这样的泛型来声明泛型变量,在类体重再添加正式的范型变量/方法后即可使用。
注意:
- 一般有T、E、K、V等字母可表示泛型,约定俗成
- 也可以有多个,用逗号间隔,如“<K,V>”
-
泛型接口
声明与泛型类无异, 重点在实现。
实现有两种方法:
<1> 用泛型类来实现泛型接口,仍返回泛型
<2>直接用确定的类型来替换字母泛型,返回的也是指定类型 -
泛型方法
可完全独立,可以不在泛型接口,泛型类中
例:
此处的“<T>”是泛型方法的明确标志
public <T> T genericMethod (T ...a){
return a[a.length/2];
}
注意:
- 不是在泛型类里的就是泛型方法
- 泛型类是使用泛型方法同样要有标志<T>???
- 在泛型类里的泛型方法完全可以和类中定义的泛型不一样
- 泛型类上声明的泛型只影响普通方法,泛型类中的泛型方法声明的泛型与泛型类上声明的泛型无关
限定类型变量的应用场景
extends 派生/继承 类或接口
需要在泛型应用过程中要求该泛型拥有某种方法或遵循某种规则
注意:
限定混用类与接口时,类必须写在最前面,且只能有一个(单继承)
如<T extends ArrayList & Comparable>
约束与局限性
- 不能实例化类型变量
如this.data = new T();
- 静态域或者方法不能引用类型变量
因为静态在声明、使用时,泛型很可能还未声明
除非该静态方法本身就是泛型方法 - 如果泛型要使用基本类型,只能用其包装类,因为基本类型不是对象
- 范型变量不能使用instance of 方法
- 注入不同类型后的泛型类,使用getClass()只能获取其原生类型而不是泛型
6Model<Double> m = new Model<>();
可行
Model<Double>[] ms = new Model<Double>[10];
不可行 - 泛型类不能派生自Throwable,
如果泛型方法声明T派生自Throwable,不能通过捕获(catch)泛型类对象T,但可通过捕获(catch)Throwable类型,再直接抛出(throws T)
泛型类型的继承规则
- 由约束与局限性中的第五条可知,存在Class A, Class B extends A
Model为范行为,Model<A>与Model<B>没有关系,即不允许Model<A> a = new Model<B>(); - 要区别于反省类之间的派生
如 ModelE<T> extends Model<T>
允许 Model<T> b = new ModelE<>();
直接例子就是List和ArrayList - 若存在public <T> void set(Model<A> m){}方法
Model<A> a = new Model<>();
Model<B> b = new Model<>();
set(a);//可行
set(b);//报错
这一条引出了泛型中通配符的使用
通配符
将泛型类型的继承规则的第3条中的public <T> void set(Model<A> m){}修改为
public <T> void set(Model<? extends A> m){}即可成立
注意:通配符用于方法或变量,而不能用于泛型类上的泛型声明
<? extends Model>限定了上界,意味着能传Model及其子类
<? super Model>限定了下界,意味着能穿Model及父类
因此对<? extends Model>进行getData(),只返回Model类型,因为只知上界;
对<? extends Model>进行setData(),无法传入其子类,因为不知下界;
对<? super Model>进行getData()返回Object,因为最上界已限定;
对<? super Model>进行setData()可传入其子类,因为已知上界。
虚拟机实现泛型
泛型擦除,jdk会用代码中提到过的最接近的类型来直接替代泛型类型,或插入强制转型的代码。
可以通过方法重载的失效来证明这一点
网友评论