第十四章_Java中的泛型
1.泛型概述
-
泛型的介绍
泛型是JDK5中引入的特性,它提供了编译时类型安全检测机制
-
泛型的好处
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
-
泛型的定义格式
- <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如: <E> <T>
- <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如: <E,T> <K,V>
-
泛型的使用时机
- 当后面我们使用的时候不确定类型,就可以定义一个泛型<E>
-
泛型使用的注意事项
- 如果创建集合的时候,没有写泛型,集合泛型就是Object
2.泛型类
-
定义格式
修饰符 class 类名<代表泛型的变量> { }
-
注意
创建对象的时候确定泛型的类型
代码演示:
public class Test {
public static void main(String[] args) {
Generic<String> g1 = new Generic<>();
g1.add("abc");
System.out.println("-----------------------");
Generic<Integer> g2 = new Generic<>();
g2.add(123);
}
}
class Generic<E>{
public void add(E e){
System.out.println("向自定义泛型类中添加了:"+e);
}
}
执行效果:
3.泛型方法
-
格式
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
-
注意
调用方法时,确定泛型的类型
代码演示:
public class Test {
public static void main(String[] args) {
Generic generic = new Generic();
generic.method("Hello Java");
System.out.println("-----------");
generic.method(123);
}
}
class Generic{
public <T> void method(T t){
System.out.println(t);
}
}
执行效果:
4.泛型接口
-
格式
修饰符 interface 接口名<代表泛型的变量> { }
-
实现类实现接口的注意事项
- 定义实现类时直接表明泛型的类型(如:Scanner)
- 定义实现类时未表明泛型的类型(如:ArrayList)
代码演示:
public class Test {
public static void main(String[] args) {
//实现类1表明了泛型的类型
GenericImpl1 impl1 = new GenericImpl1();
impl1.show("嘤嘤嘤!QAQ");
System.out.println("--------------------");
//实现类2没有表明泛型的类型
GenericImpl2 impl2 = new GenericImpl2<>();
impl2.show("一锤一个嘤嘤怪!");
impl2.show(80);
}
}
interface Generic<E>{
void show(E e);
}
//1.定义实现类时直接表明泛型的类型
class GenericImpl1 implements Generic<String>{
@Override
public void show(String s) {
System.out.println(s);
}
}
//2.定义实现类时未表明泛型的类型
class GenericImpl2<E> implements Generic<E>{
@Override
public void show(E e) {
System.out.println(e);
}
}
执行效果:
5.泛型通配符
- 泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?、
- ?表示未知通配符。
- 类型通配符: <?>
- ArrayList<?>: 表示元素类型未知的ArrayList,它的元素可以匹配任何的类型
- 但是并不能把元素添加到ArrayList中了,获取出来的也是父类类型
代码演示:
public class Test {
public static void main(String[] args) {
//创建集合
ArrayList<String> list1 = new ArrayList<>();
list1.add("二郎神");
list1.add("哮天犬");
method(list1);
System.out.println("---------------");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
method(list2);
}
//此方法想遍历上面main方法中传递过来的任意类型的集合
public static void method(ArrayList<?> list){
//由于参数传递的类型不确定,遍历时获取到的是顶级父类Object
for (Object o : list) {
System.out.println(o);
}
}
}
执行效果:
- 类型通配符上限: 类型名称 <? extends 类 > 对象名称
- ArrayListList <? extends Number>: 它表示的类型是Number或者其子类型
- 类型通配符下限: 类型名称 <? super 类 > 对象名称
- ArrayListList <? super Number>: 它表示的类型是Number或者其父类型
代码演示:
public class Test {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
ArrayList<Number> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
method(list1);
method(list2);
method(list3);
method(list4);
getElement1(list1);
getElement1(list2);//报错
getElement1(list3);
getElement1(list4);//报错
getElement2(list1);//报错
getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型通配符: 此时的泛型?,可以是任意类型
public static void method(ArrayList<?> list){}
// 泛型的上限: 此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(ArrayList<? extends Number> list){}
// 泛型的下限: 此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(ArrayList<? super Number> list){}
}
执行效果: