泛型
为什么需要泛型
- 看如下代码
ArrayList list = new ArrayList();
list.add("张三");
list.add(123234);
list.add(new Object());
for (int i = 0; i < list.size(); i++) {
Object ele = list.get(i);
if (ele instanceof String) {
System.out.println(list.get(i));
}else if (ele instanceof Integer) {
System.out.println(ele);
}else{
System.out.println(ele);
}
}
/** 输出结果:
* 张三
* 123234
* java.lang.Object@1b6d3586
*
* */
-
从上面的代码可以看出,当我们在集合中添加数据时 集合可以存储任意类型数据, 但是取数据时非常麻烦,因为取数据时,不知道是什么类型, 所以还要进行判断,和强制转换 且很容易出现“java.lang.ClassCastException”异常。那么我们就需要约束或者指定这个集合只能存储某一个类型,这时我们就需要使用泛型。
-
JDK 1.5之后出现了新的技术 —— 泛型(Generic),此技术的最大特点是类中的属性的类型可以由外部决定。泛 型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么 参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此 时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
ArrayList<String> list = new ArrayList<>(); // 那么它只能存储String类型, 无法存储其他类型
list.add("张三");
list.add("李四");
list.add(1243231432); // 编译错误
for (String name : list) {
System.out.println(name);
}
自定义泛型类
// 此时的数据类型T 就需要外部提供
class Node<T> {
private T data;
public Node(T data) {
this.data = data;
}
public void setData(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
public class GenericityDemo {
public static void main(String[] args) {
// 这里<> 里面的数据类型就对应者 Node 中的T
Node<Integer> integerNode = new Node<>(1024);
System.out.println(integerNode.getData()); // 1024
Node<String> stringNode = new Node<>("张三");
System.out.println(stringNode.getData()); // 张三
}
}
- 在泛型接口、泛型类和泛型方法的过程中, 我们常见的如 T、E、K、V等形式的参数常用于表示泛型形参,由于接收来自外部使用时候传入类型实参,从编码的角度也成为参数化类型
- 泛型只作用与代码的编译阶段, 在编译的过程中, 正确的检验泛型结果后, 会擦除泛型相关的信息将实际类型进行替换, 泛型不会出现在运行阶段。
泛型通配符
- 因为不知道调用者所传递的时什么类型泛型
- 有可能 是 Node、Node…
- 为了达到通用性, 我们使用通配符 ? 来进行接收任意泛型类型
public class GenericityDemo {
public static void getData(Node<?> node) {
System.out.println(node.getData());
}
public static void main(String[] args) {
Node<Integer> integerNode = new Node<>(1024);
Node<String> stringNode = new Node<>("张三");
getData(integerNode); // 1024
getData(stringNode); // 张三
}
}
泛型上限
- 指一个操作的泛型最大操作的父类,例如: 最大上限为: Number 类型, 此时,所能够接收的类只能是Number 及其子类
- 语法
<? extends 最大上限类>
public class GenericityDemo {
public static void getData2(Node<? extends Number> node) {
System.out.println(node.getData());
}
public static void main(String[] args) {
Node<Integer> integerNode = new Node<>(1024);
Node<Float> floatNode = new Node<>(3.14F);
Node<Long> longNode = new Node<>(23452354L);
Node<String> stringNode = new Node<> ("fawefaef");
getData2(integerNode); // 1024
getData2(floatNode); // 3.14
getData2(longNode); // 23452354
getData2(stringNode) // 报错
}
}
泛型下限
- 与泛型上限相反, 只能接收具体的类 或者是父类, 例如: 最小上限为: Integer 类型, 那么它只能接收 Integer类型 和 此类的父类
- 语法:
? super 最小上限类
public class GenericityDemo {
public static void getData2(Node<? extends Number> node) {
System.out.println(node.getData());
}
public static void main(String[] args) {
Node<Integer> integerNode = new Node<>(1024);
Node<Float> floatNode = new Node<>(3.14F);
Node<Number> numberNode = new Node<>(3423);
getData3(integerNode); // 1024
getData3(floatNode); // 编译出错
getData3(numberNode); // 3423
}
}
泛型方法
- 泛型除了在类中定义之外,还可以在方法上定义,而且在方法上使用泛型,此方法所在的类不一定是泛型的操作 类。
public class GenericityDemo {
// 第一个接收的泛型类型
// 第二个表示函数返回值
// 第三个接收T类型的数组
public static <T> T[] func(T[] array, int in1, int in2) {
T temp = array[in1];
array[in1] = array[in2];
array[in2] = temp;
return array;
}
public static void main(String[] args) {
String[] arr = {"张三", "李四", "王五", "赵六"};
System.out.println("交换前:" + Arrays.toString(arr)); // 交换前:[张三, 李四, 王五, 赵六]
String[] arr2 = func(arr, 0, 1);
System.out.println("交换后:" + Arrays.toString(arr2)); // 交换后:[李四, 张三, 王五, 赵六]
}
}
泛型的嵌套
public class GenericityDemo {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
// 获取一个Entry集合
Set<Map.Entry<Integer, String>> set = map.entrySet();
for (Map.Entry<Integer, String> entry: set) {
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}