文章目录
1 泛型简介
Java泛型是Java语言中引入的一种参数化类型机制,它可以在类、接口和方法的定义中使用类型形参(Type Parameter),从而使得代码具有更好的重用性、类型安全性和可读性。
- 从JDK1.5以后, Java引入了“参数化类型( Parameterized type) ” 的概念,允许我们在创建集合时再指定集合元素的类型, 正如: List, 这表明该List只能保存字符串类型的对象。
- JDK1.5改写了集合框架中的全部接口和类, 为这些接口、 类增加了泛型支持,从而可以在声明集合变量、 创建集合对象时传入类型实参。
- 集合没有泛型时:读取出数据需要强制类型转换,有可能抛出ClassCastException
- 集合中使用泛型:可以避免强转,避免抛出ClassCastException
List使用泛型:
@Test
public void test1() {
List<String> list = new ArrayList<>();
list.add("kong"); //因为泛型,只能存String类型
String str = list.get(0); //泛型的主要作用:避免强制类型转换
System.out.println(list);
}
@Test
public void test2() {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
//迭代器循环
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
Integer i = it.next();
System.out.println(i);
}
System.out.println("-----------");
//增强for循环
for (Integer i : list) {
System.out.println(i);
}
}
Map使用泛型:
@Test
public void test1(){
Map<String,Integer> map = new HashMap<>();
map.put("1",1);
map.put("2",1);
map.put("3",1);
//entrySet()
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry);
}
//keySet()
System.out.println("------------");
for (String s : map.keySet()) {
System.out.println(s+"="+map.get(s));
}
System.out.println("------------");
//迭代器
Iterator<String> it = map.keySet().iterator();
while (it.hasNext()) {
String key = it.next();
System.out.println(key + "=" + map.get(key));
}
}
2 自定义泛型
2.1 泛型类
语法:
public class 类名 <泛型类型1,...> {
...
}
示例:
Clazz.class
public class Clazz <T1,T2>{
private T1 t1;
private T2 t2;
public T1 getT1() {
return t1;
}
public void setT1(T1 t1) {
this.t1 = t1;
}
public T2 getT2() {
return t2;
}
public void setT2(T2 t2) {
this.t2 = t2;
}
public void f1(T1 t1, T2 t2) {
System.out.println(t1);
System.out.println(t2);
}
}
测试:
public class Test1 {
public static void main(String[] args) {
Clazz<String,Integer> clazz = new Clazz<>();
clazz.f1("kong",18);
clazz.setT1("xing");
clazz.setT2(19);
System.out.println(clazz.getT1());
System.out.println(clazz.getT2());
Clazz<String, Clz> bean2 = new Clazz<>();
bean2.f1("a", new Clz());
}
}
class Clz {
@Override
public String toString() {
return "Clz{}";
}
}
结果:
kong
18
xing
19
a
Clz{}
2.2 泛型方法
语法:
public <泛型类型...> 返回类型 方法名(泛型类型 变量名) {
...
}
示例:
public class Test1 {
public static <T1, T2> T1 f1(T1 bean, T2 arg) {
System.out.println(bean);
return bean;
}
public static void main(String[] args) {
String str = f1("abc", new Date());
Date date = f1(new Date(), "cba");
Integer i = f1(123, "aaa");
}
}
结果:
a
Thu Aug 10 19:42:11 CST 2023
123
2.3 泛型接口
语法:
public interface GenricsInterface<T>{
...
}
//实现类
public class Clazz implements GenricsInterface<String> {
...
}
示例:
接口GenericsInterface
public interface GenericsInterface<T> {
public T getValue();
}
实现类IntegerImpl
public class IntegerImpl implements GenericsInterface<Integer> {
@Override
public Integer getValue() {
return 123456;
}
}
实现类StringImpl
public class StringImpl implements GenericsInterface<String> {
@Override
public String getValue() {
return "hello generics interface";
}
}
测试
public class Test1 {
public static void main(String[] args) {
GenericsInterface<String> g1 = new StringImpl();
System.out.println(g1.getValue());
GenericsInterface<Integer> g2 = new IntegerImpl();
System.out.println(g2.getValue());
}
}
结果:
hello generics interface
123456
3 泛型通配符
3.1 通配符的上边界
上限通配符(Upper Bounds Wildcards):使用extends
关键字来定义上限通配符,表示泛型参数必须是指定类型或其子类。
例如:<? extends T>
在类型参数中使用 extends 表示这个泛型中的参数必须是 T 或者 T 的子类
示例:
public class Test1 {
public static void f1(List<? extends Father> list) {
}
public static void main(String[] args) {
List<GrandFather> grandFatherList = new ArrayList<>();
List<Father> fatherList = new ArrayList<>();
List<Mother> motherList = new ArrayList<>();
List<Son> sonList = new ArrayList<>();
List<Daughter> daughterList = new ArrayList<>();
f1(sonList);
f1(daughterList);
f1(fatherList);
//f1(motherList);
}
class GrandFather {
}
class Father extends GrandFather {
}
class Mother {
}
class Daughter extends Father {
}
class Son extends Father {
}
}
在以上示例中,尝试将 sonList 和 daughterList 传递给 f1 方法,由于它们都是 Father 类型的列表的子类,与 List<? extends Father> 匹配。因此,这些传递是合法的。而motherList不是Father 类型的列表的子类,所以无法传递。
3.2 通配符的下边界
下限通配符(Lower Bounds Wildcards):使用super
关键字来定义下限通配符,表示泛型参数必须是指定类型或其父类。
例如:<? super T>
在类型参数中使用 super 表示这个泛型中的参数必须是 T 或者 T 的父类
示例:
public class Test2 {
public static void f1(List<? super Father> list) {
}
public static void main(String[] args) {
List<GrandFather> grandFatherList = new ArrayList<>();
List<Father> fatherList = new ArrayList<>();
List<Mother> motherList = new ArrayList<>();
List<Son> sonList = new ArrayList<>();
List<Daughter> daughterList = new ArrayList<>();
f1(fatherList);
f1(grandFatherList);//? super Father:表示对象边界是Father和Father的父类
//f1(motherList);
}
class GrandFather {
}
class Father extends GrandFather {
}
class Mother {
}
class Daughter extends Father {
}
class Son extends Father {
}
}
在以上示例中,尝试将 fatherList和 grandFatherList传递给 f1 方法,由于它们是 Father 类型或其父类,与 List<? super Father>匹配。因此,这些传递是合法的。而motherList不是Father 类型的父类,所以无法传递。
3.3 泛型边界在接口中的应用
声明接口的泛型只能使用T extends Type
示例:
public class Test1 {
class Impl1 implements GenricsInterface<Father> {
@Override
public Father getValue() {
return null;
}
}
class Impl2 implements GenricsInterface<Mother> {
@Override
public Mother getValue() {
return null;
}
}
class Impl3 implements GenricsInterface<String> {
@Override
public String getValue() {
return null;
}
}
class Impl4 implements GenricsInterface<GrandFather> {
@Override
public GrandFather getValue() {
return null;
}
}
class Impl5 implements GenricsInterface<Son> {
@Override
public Son getValue() {
return null;
}
}
class Impl6 implements GenricsInterface<Daughter> {
@Override
public Daughter getValue() {
return null;
}
}
interface GenricsInterface<T extends Father> {
public T getValue();
}
class GrandFather {
}
class Father extends GrandFather {
}
class Mother {
Father father;
}
class Daughter extends Father {
}
class Son extends Father {
}
}
在以上示例中提供了一个泛型接口 GenricsInterface,其类型参数 T 必须是 Father 类型或其子类,所以Impl2、Impl3、Impl4都不能实现该接口。
3.4 无界通配符
?
表示传入任意类型
示例:
public static void f2(List<?> list) {
}
public void test1() {
List<GrandFather> grandFatherList = new ArrayList<>();
List<Father> fatherList = new ArrayList<>();
List<Mother> motherList = new ArrayList<>();
List<Son> sonList = new ArrayList<>();
List<Daughter> daughterList = new ArrayList<>();
f2(sonList);
f2(daughterList);
f2(fatherList);
f2(grandFatherList);
f2(motherList);
f2(new ArrayList<String>());
}
4 泛型擦除
泛型擦除是Java编程语言中的一种特性,它指的是在编译过程中将泛型类型信息擦除,使得泛型类型在运行时不可见。
Java的泛型是伪泛型,为什么说Java的泛型是伪泛型呢?因为在编译期间,所有的泛型信息都会被擦除掉,我们常称为泛型擦除。
Java中的泛型是通过类型擦除来实现的。在编译过程中,所有的泛型类型参数都会被擦除为它们的上界或者Object 类型。这意味着在运行时,无法获取到泛型类型的具体信息。
示例:
@Test
public void test1() {
List<String> stringList = new ArrayList<String>(); //ArrayList.class
stringList.add("泛型");
List<Integer> integerList = new ArrayList<Integer>(); //ArrayList.class
integerList.add(1);
System.out.println(stringList.getClass());
System.out.println(integerList.getClass());
System.out.println(stringList.getClass() == integerList.getClass()); //true
}
结果:
class java.util.ArrayList
class java.util.ArrayList
true