一、泛型引入
1、为什么需要泛型
传统方式存在的问题
-
不能对加入到集合中的数据类型进行约束
-
遍历时,需要进行类型转换
泛型的理解与好处
-
编译时能检查添加元素的类型
-
能减少类型转换的次数
2、泛型初体验
(1)说明
- 这里以 Dog 类为例
ArrayList<Dog> list = new ArrayList<Dog>();
-
不使用泛型:放入集合时,Dog 类型会先转换为 Object 类型,取出时,需要转换为 Dog 类型
-
使用泛型:放入集合和取出时,不需要进行类型转换
(2)演示
package com.my.test;import java.util.ArrayList;
import java.util.Iterator;public class GenericTest {public static void main(String[] args) {ArrayList<Dog> list = new ArrayList<>();list.add(new Dog("小黄", 2));list.add(new Dog("小汪", 3));list.add(new Dog("小多", 4));System.out.println("========== 增强 for 循环遍历 ==========");for (Dog dog : list) {System.out.println(dog);}System.out.println("========== 迭代器循环遍历 ==========");Iterator<Dog> iterator = list.iterator();while (iterator.hasNext()) {Dog dog = iterator.next();System.out.println(dog);}}
}class Dog {String name;int age;public Dog(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +'}';}
}
- 输出结果
========== 增强 for 循环遍历 ==========
Dog{name='小黄', age=2}
Dog{name='小汪', age=3}
Dog{name='小多', age=4}
========== 迭代器循环遍历 ==========
Dog{name='小黄', age=2}
Dog{name='小汪', age=3}
Dog{name='小多', age=4}
3、泛型概述
-
在类声明或实例化时制定好需要的具体类型
-
可以保证在编译时没有警告,运行时就不会产生 ClassCastException(类型转换异常)
-
通过标识表示类中某个属性的类型,某个方法的参数类型或返回值类型
4、泛型语法
- 自定义泛型类
class 【类名】 <T> {}
- 自定义泛型接口
class 【接口名】 <T> {}
- 自定泛型方法
【修饰符】 <T> 【返回数据类型】 【方法名】(【参数列表】) {}
5、注意事项
-
T 表示类型(Type 的缩写,也可以为 K、V 等),且必须是引用类型
-
指定泛型具体类型后,可以传入该类型或者其子类类型
-
泛型的简写
// 完整写法
ArrayList<Dog> list = new ArrayList<Dog>();
// 简写
ArrayList<Dog> list = new ArrayList<>();
- 这样写 ArrayList<> list = new ArrayList<>();,默认指定泛型具体类型为 Object 类型
二、自定义泛型类
1、基本介绍
class 【类名】 <T> {}
2、注意事项
- 普通成员可以使用泛型
T t;
- 使用泛型的数组不能初始化,因为不能确定 T 的类型,无法开辟空间,在指定泛型的具体类型后才能初始化
// 错误写法
T arr[] = new T[];
-
静态成员不能使用泛型,因为静态成员与类相关,类加载时,静态成员无法确定泛型的具体类型,无法完成初始化
-
泛型的具体类型在创建对象时确定
-
创建对象时,没有指定泛型的具体类型,默认为 Object 类型
3、演示
- CustomGenericsClassTest.java
package com.my.test;public class CustomGenericsClassTest {public static void main(String[] args) {Person<String> person1 = new Person<>("jack");Person<Integer> person2 = new Person<>(10);person1.show();person2.show();}
}class Person<T> {T t;public Person(T t) {this.t = t;}public void show() {System.out.println(this.t);}
}
- 输出结果
jack
10
三、自定义泛型接口
1、基本介绍
class 【接口名】 <T> {}
2、注意事项
-
接口中静态成员不能使用泛型
-
接口的泛型的具体类型,在继承接口或实现接口时确定
-
没有指定泛型的具体类型,默认为 Object 类型
3、演示
- CustomGenericInterfaceTest.java
package com.my.test;public class CustomGenericInterfaceTest {public static void main(String[] args) {new Student().show("jack");}
}class Student implements Person<String> {@Overridepublic void show(String s) {System.out.println(s);}
}interface Person<T> {public void show(T t);
}
- 输出结果
jack
四、自定义泛型方法
1、基本介绍
【修饰符】 <T> 【返回数据类型】 【方法名】(【参数列表】) {}
2、注意事项
-
泛型方法可以定义在普通类和泛型类中
-
当泛型方法被调用时,泛型的具体类型被确定
-
public void method(T t) {} 不是泛型方法,而是使用了泛型
3、演示
- CustomGenericMethodTest.java
package com.my.test;public class CustomGenericMethodTest {public static void main(String[] args) {Cat<String> cat = new Cat<>();cat.show(10, "tom");}
}class Cat<K> {public<T> void show(T t, K k) {System.out.println(t.getClass());System.out.println(k.getClass());}
}
- 输出结果
class java.lang.Integer
class java.lang.String
五、泛型的继承与通配符
1、基本介绍
- 泛型不具备继承性
// 错误写法
List<Object> list1 = new ArrayList<String>();
-
<?>:支持任意类型
-
<? extends A>:支持 A 类以及 A 类的子类,规定了泛型的上限
-
<? super A>:支持 A 类以及 A 类的父类,规定了泛型的下限
2、演示
- GenericExtends.java
package com.my.test;import java.util.ArrayList;
import java.util.List;public class GenericExtends {public static void main(String[] args) {List<Object> list1 = new ArrayList<>();List<String> list2 = new ArrayList<>();List<AA> list3 = new ArrayList<>();List<BB> list4 = new ArrayList<>();List<CC> list5 = new ArrayList<>();printList1(list1);printList1(list2);printList1(list3);printList1(list4);printList1(list5);// printList2(list1); // ×
// printList2(list2); // ×printList2(list3);printList2(list4);printList2(list5);printList3(list1);
// printList3(list2); // ×printList3(list3);
// printList3(list4); // ×
// printList3(list5); // ×}public static void printList1(List<?> list) {for (Object o : list) {System.out.println(o);}}public static void printList2(List<? extends AA> list) {for (Object o : list) {System.out.println(o);}}public static void printList3(List<? super AA> list) {for (Object o : list) {System.out.println(o);}}
}class AA {}class BB extends AA {}class CC extends BB {}