版权声明:本文为HCG原创文章,未经博主允许不得转载。请联系[email protected] https://blog.csdn.net/qq_39455116/article/details/87879779
01、泛型是什么?
A:泛型其实就是在定义类、接口、方法的时候不局限地指定某一种特定类型,而让类、接口、方法的调用者来决定具体使用哪一种类型的参数。
B:比如一个水杯生产的时候不用指定它将来干什么?而是由将来的使用者决定放入什么。
C:其实就是一句话:我是一个泛型队列,狗可以站进来,猫也可以站进来,但最好不要既站猫,又站狗!
别让猫狗站在队列里
注:在Java中,经常用T、E、K、V等形式的参数来表示泛型参数。
T:代表一般的任何类。
E:代表 Element 的意思,或者 Exception 异常的意思。
K:代表 Key 的意思。
V:代表 Value 的意思,通常与 K 一起配合使用。
02、不使用泛型,放入猫狗
package fanxing;
import java.util.HashMap;
import java.util.Map;
public class MaoGou {
class Dog {
}
class Cat {
}
public static void main(String[] args) {
MaoGou maoGou = new MaoGou();
Map map = new HashMap();
map.put("dog", maoGou.new Dog());
map.put("cat", maoGou.new Cat());
Cat cat = (Cat) map.get("dog");
Dog dog = (Dog) map.get("cat");
System.out.println(dog + "dog");
System.out.println(cat + "cat");
}
}
Exception in thread "main" java.lang.ClassCastException:
fanxing.MaoGou$Dog cannot be cast to fanxing.MaoGou$Cat
编译阶段没有问题,运行时报错,就是放入的是猫,不能取出的是狗
2.1那如何解决上述问题呢?用泛型!!!
直接在map定义的时候指定要放入的对象
package fanxing;
import java.util.HashMap;
import java.util.Map;
public class MaoGou {
class Dog {
}
class Cat {
}
public static void main(String[] args) {
MaoGou maoGou = new MaoGou();
Map map = new HashMap();
// map.put("dog", maoGou.new Dog());
// map.put("cat", maoGou.new Cat());
// Cat cat = (Cat) map.get("dog");
// Dog dog = (Dog) map.get("cat");
// System.out.println(dog + "dog");
// System.out.println(cat + "cat");
Map<String, Cat> map2 = new HashMap<>();
map2.put("cat2", maoGou.new Cat());
Cat cat2 = map2.get("cat2");
System.out.println("cat2 " + cat2);
}
}
03、泛型的存在阶段是编译阶段还是运行阶段?
有人说,Java的泛型做的只是表面功夫——泛型信息存在于编译阶段(狗队在编译时不允许站猫),运行阶段就消失了(运行时的队列里没有猫的信息,连狗的信息也没有)——这种现象被称为“类型擦除”。
package fanxing;
import java.util.HashMap;
import java.util.Map;
public class MaoGou {
class Dog {
}
class Cat {
}
public static void main(String[] args) {
MaoGou maoGou = new MaoGou();
Map map = new HashMap();
Map<String, Cat> catMap = new HashMap<>();
Map<String, Dog> dogMap = new HashMap<>();
System.out.println(catMap.getClass());
System.out.println(dogMap.getClass());
}
}
输出结果:
class java.util.HashMap
class java.util.HashMap
分析:
也就是说,Java代码在运行的时候并不知道catMap的键位上放的是Cat,dogMap的键位上放的是Dog。
即满足了上述说的《类型擦除》
3.1 那么,试着想一些可怕的事情:
既然运行时泛型的信息被擦除了,而反射机制是在运行时确定类型信息的,那么利用反射机制,是不是就能够在键位为Cat的Map上放一只Dog呢?
package fanxing;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class MaoGou {
class Dog { }
class Cat { }
public static void main(String[] args) {
MaoGou maoGou = new MaoGou();
Map map = new HashMap();
Map<String, Cat> catMap = new HashMap<>();
try {
Method method =catMap.getClass().getDeclaredMethod("put",Object.class,Object.class);
method.invoke(catMap,"dog",maoGou.new Dog());
System.out.println(catMap);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
输出结果:{dog=fanxing.MaoGou$Dog@3f99bd52}
看到没?我们竟然在键位为Cat的Map上放了一只Dog!
注:Java的设计者在JDK 1.5时才引入了泛型,但为了照顾以前设计上的缺陷,同时兼容非泛型的代码,
不得不做出了一个折中的策略:编译时对泛型要求严格,运行时却把泛型擦除了——要兼容以前的版本,
还要升级扩展新的功能,真的很不容易!
04、自定义泛型
package fanxing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class PetHouse<T> {
private List<T> list;
public PetHouse() {
}
public void add(T item) {
list.add(item);
}
public T get() {
return list.get(0);
}
public static void addTest(List<?> list) {
Object o = new Object();
// list.add(o); // 编译报错
// list.add(1); // 编译报错
// list.add("ABC"); // 编译报错
list.add(null);
}
}
05、泛型通配符
? 通配符类型 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List<?>
无边界的通配符的主要作用就是让泛型能够接受未知类型的数据.
<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类
<? super T> 表示类型下界(Java Core中叫超类型限定),
表示参数化类型是此类型的超类型(父类型),直至Object
注意: 你可以为一个泛型指定上边界或下边界, 但是不能同时指定上下边界.
上界限定符接受 extends 后面类的本身与其子类, 下界限定符接受 super 后面类的本身与其父类。无限定通配符接受任何类。
5.1 ?不能使用add()方法,NULL除外
public static void addTest(List<?> list) {
Object o = new Object();
// list.add(o); // 编译报错
// list.add(1); // 编译报错
// list.add("ABC"); // 编译报错
list.add(null);
}
所以“?”声明的集合,不能往此集合中添加元素,所以它只能作为生产者(亦即它只能被迭代),如下:
public static void main(String[] args) {
List<?> names = new ArrayList<String>() {
{
for (int i = 0; i < 10; i++) {
add("A" + i);
}
}
};
System.out.println(names.toString());
// 只能以Object迭代元素
for (Object name : names) {
System.out.println(name);
}
}
5.2 “? extends T”也不能添加元素 只能存指定类型或者其子类
public static void main(String[] args) {
List<? extends String> names = new ArrayList<String>() {
{
for (int i = 0; i < 10; i++) {
add("A" + i);
}
}
};
System.out.println(names.toString());
// 相比?更能准确定位元素类型
for (String name : names) {
System.out.println(name);
}
}
因为Integer Double Long都是Number的子类
List<? extends Number> numbers =new ArrayList<Number>(){
{
add(1);
add(1.2);
add((long)1);
//add("2"); 报错
}
};