泛型 : 要解决类型的安全问题, 如果使用Object类型会带来类型的损失。典型的应用就是在集合中, 集合中理论上可以保存任意对象,实际上我们应该让它泛型化,集合类<元素类型>, 添加元素只能添加指定类型,获取元素时一定能获取指定类型的对象,不需要造型。
package com.atguigu.javase.exer;
import org.junit.Test;
class Person<A> { //A为成员泛型
private int id;
private A scret;//成员属性可以使用泛型A
public A method1(A a) {//成员方法可以使用泛型A
return a;
}
//public static void method2(A a){} 静态环境中不可以使用泛型类型
}
public class Exer {
@Test
public void test1 () {
Person<Integer> person1 = new Person<Integer>();
Integer integer = person1.method1(250);//*********************************①
System.out.println("integer = " + integer);//250
Person<String> person2 = new Person<String>();
String s = person2.method1("123456");
System.out.println("s = " + s);//123456
}
}
代码中①处,person1.method1(250); 调用方法method1后,使用快捷键 Alt + Enter 自动添加声明参数后,如下:
泛型会从从添加的参数自动判断变量类型。从上述代码中可知,成员泛型不能在类方法(静态环境)中使用,在静态方法中使用泛型如下:
public static <B> B method2(B b) {
return b;
}
//Float aFloat = Person.method2(123.6f); 输出结果:123.6
List中的toArray方法在使用时,会返回一个Object类型的数组,但是List集合在经过泛型约束后,只有一种数据类型,此时可以使用 toArray(T [] a)方法,返回泛型类型数组。
使用 toArray(T [] a)方法,返回泛型类型数组,需要new 一个新数组,数组是空的也没有问题,因为它的唯一作用就是让方法感知泛型类型是什么即可,如果new出来的新数组长度不为空,假设为2,则 toArray(T [] a)方法返回的数组长度为List.size() + 2,后两个存储的数据为null,代码如下:
@Test
public void test2() {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add((int)(Math.random() * 20));
}
System.out.println(list);
System.out.println(list);
Integer[] integers = list.toArray(new Integer[]{});
Arrays.toString(integers);
}
/*输出结果:
[11, 16, 5, 5, 13, 9, 8, 13, 2, 5]
[11, 16, 5, 5, 13, 9, 8, 13, 2, 5]
泛型的多态
①无限通配符 -------?
泛型的多态如下,?表示泛型通配符,一旦只使用泛型通配符,表示此对象权限只读,即不能添加、删除或者修改元素(null 除外,因为null的类型未知),只能访问对象的内容。
public void test3() {
List<?> list = new ArrayList();//?表示泛型通配符,类型未知
//list.add(200);不能添加已知类型
//list.add("123456");
list.add(null);//可以添加,因为null的类型未知
Object o = list.get(0);
System.out.println(o);
}
当获取集合list中的元素时,自动获取变量的类型为Object。
②有限通配符 super xxx>
以<? super Number> 为例,有限的通配符: ? 表示未知,super表示父类, Number及其未知父类类型, 此类型一定可以兼容Number,下限是Number, 上限未知, super Number> 适用于添加元素,但是不适合取元素,因为读出来的元素默认类型是Object,不知道其确切类型,无意义。
@Test
public void test4() {
List<? super Number> list = new ArrayList<>();
list.add(100);
list.add(100.25f);
list.add((byte)127);
list.add(null);
System.out.println(list);
}//[100, 100.25, 127, null]
②有限通配符 extends xxx>
以<? extends Number> 为例,有限的通配符: ? 表示未知,extends 表示子类, Number及其未知子类类型,上限是Number, 下限未知, extends Number> 适用于读取元素,但是不适合添加元素。
@Test
public void test5() {
List<? extends Number> list = new ArrayList<>();
//list.add(500);不能添加已知元素类型
Number number = list.get(0);
}
注意事项
小练习
取出 List 集合、Set 集合中的最小值,元素类型为Integer、double 和 String 类型。
分析:写一个函数,min() ,因为数据类型中含有String类型,所以两个函数的返回值类型为Comparable,而 List 和 Set 为 Collection 的子类,因此,形参为 Collection。方法头如下:
public Comparable min(Collection extends Comparable> collection)
public Comparable min (Collection<? extends Comparable> collection) {
Iterator<? extends Comparable> iterator = collection.iterator();
if (!iterator.hasNext()) {
return null;
}
Comparable tmp = iterator.next();
while (iterator.hasNext()) {
Comparable next = iterator.next();
if (next.compareTo(tmp) < 0) {
tmp = next;
}
}
return tmp;
}
@Test
public void test6() {
Set<String> set = new HashSet<>();
set.add("123456");
set.add("nkkmijkk");
set.add("模式");
set.add(";plpo");
System.out.println(set);
Comparable min1 = min(set);
System.out.println(min1);
/*输出结果:
[nkkmijkk, 模式, 123456, ;plpo]
123456
*/
List<Double> list1 = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list1.add(Math.random() * 100);
}
System.out.println(list1);
Comparable min2 = min(list1);
System.out.println(min2);
/*输出结果:
[60.5256953154801, 90.11505579550146, 28.860120945706058, 77.1983740907938, 19.60048352986493, 69.21936642849151, 30.878472902293108, 39.013298790400256, 18.38974097260038, 23.36037297225785]
18.38974097260038
*/
List<Integer> list2 = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list2.add((int)(Math.random() * 100));
}
System.out.println(list2);
Comparable min3 = min(list2);
System.out.println(min3);
/*输出结果:
[74, 50, 16, 44, 39, 17, 82, 26, 35, 51]
16
*/
}
上述代码中调用 min() 函数时,自动添加变量类型为 Comparable ,如下图: