一:泛型概念
泛型就是参数化类型
适用于多种数据类型执行相同的代码
泛型中的类型在使用时指定
泛型归根到底就是“模版”
优点:使用泛型时,在实际使用之前类型就已经确定了,不需要强制类型转换。
二:命名规则
JDK 中文档经常能看到T、K、V、E、N等类型参数,而我们在编写泛型相关代码时,这些符号都可以随意使用,实际上还可以使用 JDK 文档中从来没用过的符号,如A、B、C等,但却极力不推荐这样做
JDK 文档中各符号的含义:
T:类型
K:键
V:值
E:元素(如集合类中的元素全部用该符号表示)
N:Number
三:自定义泛型
1:泛型类
public class Generic<T> {
private T key ;
public Generic(T key) {
this.key = key;
}
public T getKey(){
return key;
}
}
测试
Generic<String> stringGeneric = new Generic<>("haha");
Generic<Integer> integerGeneric = new Generic<>(123);
System.out.println(stringGeneric.getKey());
System.out.println(integerGeneric.getKey());
结果:
haha
123
在这个泛型类使用的时候,传入实参的话就在初始化的时候限制了类型,编译器会自动识别,如果不符合,会出现错误。
image.png
当然我们也可以不使用实参
Generic t1 = new Generic(234);
Generic t2 = new Generic("aaa");
Generic t3 = new Generic(false);
System.out.println(t1.getKey()+"=="+t2.getKey()+"=="+t3.getKey());
结果:
234==aaa==false
2:泛型接口
public interface Generator<T> {
T next();
}
实现泛型接口
//实现泛型类的时候,没有传入实参,类本身也需要声明泛型,否则会报错
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
//
class FruitGenerator1 implements Generator{
@Override
public Object next() {
return null;
}
}
class FruitGenerator2 implements Generator<String>{
@Override
public String next() {
return null;
}
}
3:泛型方法
在上述泛型类的例子中
//此方法并不是泛型方法,只是返回值是在声明泛型类的时候已经声明过的泛型
public T getKey(){
return key;
}
泛型方法例子
public class Gen<T> {
//此方法也不是泛型方法,只是使用了泛型参数
public void show_1(T t){
System.out.println(t.toString());
}
//**该泛型方法中的T泛型和泛型类中T是两种不同的类型**
public <T> void show_2(T t){
System.out.println(t.toString());
}
//在泛型方法中声明了泛型E,因此,在参数中可以使用E,即使泛型类中没有声明该泛型
public <E> void show_3(E e){
System.out.println(e.toString());
}
}
测试
public static void main(String[] args){
Apple apple = new Apple();
Person person = new Person();
Gen<Apple> g1 = new Gen<>();
g1.show_1(apple);
//该调用编译器会报错,因为g1在初始化的时候已经声明泛型为实际参数Apple了,show_1方法会使用泛型类传入的实际参数,因此person类型不匹配
//g1.show_1(person);
g1.show_2(apple);
g1.show_2(person);
g1.show_3(person);
g1.show_3(apple);
}
结果:
Apple{}
Apple{}
Person{}
Person{}
Apple{}
四:泛型的边界
public static void test(Generic<? extends Number> obj){
System.out.println(obj.getClass());
}
Generic<Float> f1 = new Generic<>(123f);
Generic<Integer> integerGeneric = new Generic<>(123);
Generic<String> stringGeneric = new Generic<>("aaa");
GenericTest.test(integerGeneric);
GenericTest.test(f1);
//GenericTest.test(stringGeneric);
结果
class com.ren.practise.generic.Generic
class com.ren.practise.generic.Generic
上述注释掉的内容会报如下错误:
image.png
五:泛型通配符
public static void setKeyValue(Generic<?> obj){
System.out.println(obj.getClass());
}
Generic<Number> n1 = new Generic<Number>(123);
Generic<Integer> n2 = new Generic<>(123);
setKeyValue(n1);
//setKeyValue(n2);
结果:
class com.ren.practise.generic.Generic
上述注释掉的内容会报如下错误:
image.png
使用通配符
public static void setKeyValue(Generic<?> obj){
System.out.println(obj.getClass());
}
Generic<Number> n1 = new Generic<Number>(123);
Generic<Integer> n2 = new Generic<>(123);
setKeyValue(n1);
setKeyValue(n2);
结果:
class com.ren.practise.generic.Generic
class com.ren.practise.generic.Generic
? 可以当做一种特殊的实际参数
六:泛型特性
ArrayList<String> a2 = new ArrayList<>();
ArrayList<Integer> a3 = new ArrayList<>();
Class c1 = a2.getClass();
Class c2 = a3.getClass();
System.out.println(c1.equals(c2));
结果:
true
泛型只在编译期有效,编译过程中,如果检查正确,会将泛型相关信息擦除。