一, 泛型: 类名, 参数化.
二, 有时在编程时 (定义类, 定义接口, 定义方法时), 对于要使用的对象, 暂不知道其具体是什么类型, 可用 "自定义的标识符"(占位符) 来代替, 我们称之为类型参数.
三, 定义泛型类:
- class Point<T1, T2>{ // 在定类时, 在类名后面, 给出 "类型参数"T1,T2. 在下面的定义体中, 就可以使用T1,T2作为 "类型名" 的替代符了(占位符)
- T1 x;
- T2 y;
- public T1 getX() {
- return x;
- }
- public void setX(T1 x) {
- this.x = x;
- }
- public T2 getY() {
- return y;
- }
- public void setY(T2 y) {
- this.y = y;
- }
- }
使用上述定义的泛型类:
Point<Integer, Integer> p1 = new Point<Integer, Integer>();// 实例化泛型类. 泛型类在实例化时必须指出具体的类型, 也就是向类型参数传值
(1)在 <T1, T2>,T1, T2 是自定义的标识符, 称之为类型参数. T1, T2 只是数据类型的占位符, 运行时会被替换为真正的数据类型.
(2)类型参数必须是一个合法的标识符, 习惯上使用单个大写字母, 通常情况下, K 表示键, V 表示值, E 表示异常或错误, T 表示一般意义上的数据类型.
(3)类型参数, 在定义类时, 需要在所定义的类名后面给出. 一旦给出了类型参数, 在类的定义体中, 就可以使用它了, 如
(4)传值参数 (我们通常所说的参数) 由小括号包围, 如 (int x, double y), 类型参数 (泛型参数) 由尖括号包围, 多个参数由逗号分隔, 如 < T> 或 <T, E>.
(5)泛型类在实例化时必须指出具体的类型, 也就是向类型参数传值, 格式为:
- className variable<dataType1, dataType2> = new className<dataType1, dataType2>()
- ; // 实例化泛型类(即调用)
除了定义泛型类, 还可以定义泛型方法, 例如, 定义一个打印坐标的泛型方法:
四, 定义泛型方法
- public static <V1, V2> void printPoint(V1 x, V2 y){
- // 类型参数 < V1,V2 > 需要放在修饰符后面, 方法 (名) 回值类型前面
- V1 m = x; V2 n = y; System.out.println("This point is:" + m + "," + n);
- }
泛型方法的使用:
- Point<Integer, Integer> p1 = new Point<Integer, Integer>();// 先实例化泛型类, 取得其引用
- p1.printPoint(p1.getX(), p1.getY());// 用类的引用, 调用方法
(1)与使用泛型类不同, 使用泛型方法时不必用 < V1,V2 > 指明参数类型, 编译器会根据传递的参数自动查找出具体的类型.
(2)泛型方法与泛型类没有必然的联系, 泛型方法有自己的类型参数, 在普通类中也可以定义泛型方法.
泛型方法 printPoint() 中的类型参数 V1,V2 与泛型类 Point 中的 T1, T2 没有必然的联系
五, 泛型接口
在 Java 中也可以定义泛型接口, 这里不再赘述, 仅仅给出示例代码:
- // 定义泛型接口
- interface Info<T> {
- public T getVar();
- }
- // 实现接口
- class InfoImp<T> implements Info<T> {
- //< >号, 接口名, 后面
- private T var;
- public InfoImp(T var) {
- // 定义泛型构造方法
- this.setVar(var);
- }
- public void setVar(T var) {
- this.var = var;
- }
- public T getVar() {
- return this.var;
- }
- }
六, 类型擦除
如果在使用泛型时没有指明数据类型, 那么就会擦除泛型类型. 如:
定义了型类:
- class Point<T1, T2>{
- public T1 getX() {
- return x;
- }
- ......
- }
使用此类时, 没有用 <> 指定具体类型:
- Point p = new Point(); // 类型擦除
- int x = (Integer)p.getX(); // 向下转型
因为在使用泛型时没有指明数据类型, 为了不出现错误, 编译器会将所有数据向上转型为 Object, 所以在取出坐标使用时要向下转型,
七, 限制泛型的可用类型:
对于泛形参数 < T1,T2>, 不想其可选任意类型, 而只想其选某几个类型, 如, 让它只能接受 Number 及其子类(Integer,Double,Character 等)
则可用 "参数 T 是某类的子类" 的方法:
- public <T extends Number> T getMax(T array[]){
- T max = null;
- for(T element : array){
- max = element.doubleValue()> max.doubleValue() ? element : max;
- }
- return max;
- }
(1)<T extends Number> 表示 T 只接受 Number 及其子类, 传入其他类型的数据会报错. 这里的限定使用关键字 extends, 后面可以是类也可以是接口.
但这里的 extends 已经不是继承的含义了, 应该理解为 T 是继承自 Number 类的类型, 或者 T 是实现了 XX 接口的类型.
八, 泛型通配符?
1,T 通常用于类后面和 方法修饰符 (返回值前面) 后面 , 所以在使用之前必须确定类型, 即新建实例时要制定具体类型,
而? 通配符通常用于变量 , 在使用时给定即可
2,java 泛型中, 通配符? 表示未知类型, 等同于<? extends Object>,<? extends T > 是上边界限定通配符,<? super T > 是下边界限定通配符
- 3,"上界通配符(Upper Bounds Wildcards)":
- Plate<? extends Fruit> //
4, 在 java 泛型中,? 表示通配符, 代表未知类型,<? extends Object > 表示上边界限定通配符,<? super Object > 表示下边界限定通配符.
5, 下界类型通配符
List<? super Integer>
九, 注意: 一般的应用开发中泛型使用较少, 多用在框架或者库的设计中
来源: https://www.cnblogs.com/godzoo/p/9785642.html