🍁
先说结论:
- T、E、K、V、?本质都是通配符。用于定义泛型类、泛型方法、泛型接口…换成其他字母也行,只是这几个字母是一种编码约定。
- T,即type,表示一个具体的Java类型
- E,即element,常用于集合,如List<E>、Set<E>
- K V 即key . value,常用于Map的键值对
- ? 表示不确定的Java类型(详细看后面)
1、初识泛型
Object类型可以接收任意类型,但实际应用中会有类型转换的问题。先看一下没有泛型时,写代码:
List myList = new ArrayList();
myList.add(new Integer(23));
我看到集合中放进去了一个Integer类型的数据,但我get(index)拿到的是Object类型,必须向下转型为Integer。
Integer i = (Integer) myList.get(0);
引入泛型,即为集合中的元素指定一个统一的类型
List<Intger> myList = new ArrayList<>();
myList.add(new Integer(23));
//此时,不再需要转型
Integer i = myList.get(0);
泛型总结即:
- 在JDK5.0之后的新特性–泛型
- 泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的,运行阶段泛型没用
- 写了泛型而不用,那就是Object类型
- 泛型的优点是:集合中存储的元素类型统一,从集合中取出的元素类型是泛型指定的类型,不需要进行大量的向下转型
- 泛型的缺点是:导致集合中存储的元素缺乏多样性,但日常开发中类型统一,所以泛型应用较多
泛型在日常开发中大量用在集合当中,即统一元素类型,避大量向下转型,也就是上面总结的。除了这一点,泛型的使用方式还有:
- 泛型类
- 泛型方法
- 泛型接口
这时,泛型的作用是:把明确类型的工作推迟到创建对象或者调用方法时才明确。
2、泛型类
定义泛型类,语法:
修饰符 class 类名<泛型通配符>{
}
例1:
//类中有属性类型为T
//这里的<T>中的T是标识符,随便写,但<T>常用些
@Getter
@Setter
public class MyClass<T> {
private Integer id;
private T data;
private T info;
}
此时,使用泛型类:
MyClass<String> myClass = new MyClass<>();
myClass.setData("code-9527");
例2:
//类中有方法的形参类型为T
public class GenericTest<T>{
public void doSome(T x){
System.out.println(x);
}
public static void main(String[] args) {
GenericTest<String> g1 = new GenericTest<>();
g1.doSome("code");
GenericTest<Integer> g2 = new GenericTest<>();
g2.doSome(9527);
//写了泛型而不用,那就是Object类型
GenericTest g3 = new GenericTest();
//这里传参是Object类型
g3.doSome();
}
}
当然,也可以指定多泛型:
public class MoreGeneric<T,N,A,B>{
private T data;
private N num;
private A arg;
public static void doSome(T t){
//doSome
}
}
3、泛型方法
泛型方法,即调用时才指明泛型的具体类型。语法格式:
修饰符 <泛型通配符变量> 返回值类型 方法名(形参){
}
- public和返回值类型之间的
<T>是声明此方法为泛型方法
<T>表明在方法可以使用声明的泛型类型
,即T类型- 泛型方法可以声明为 static 的
例1:
public <T> void doSome(T t){
System.out.println("doSome...");
}
例2:
public class GenericDemo{
public <T> T genericMethod(T t){
System.out.println(t.getClass());
System.out.println(t);
return t;
}
}
调用泛型方法:
例3:泛型方法中的变长参数
public class GenericDemo{
public <A> A[] argsMethod(A ... args){
for(A arg : args){
System.out.println(arg);
}
return args; //返回泛型数组
}
}
//调用 new GenericDemo().argsMethod('a','b','c','d','e');
例4:静态泛型方法
public static <T,E> String staticGeneric(String str,T t,E e){
String res = "";
res += str + "--" + t + "--" + e'
return res;
}
//调用还是直接类名.方法名
4、泛型接口
和定义泛型类相似,语法:
修饰符 interface 接口名<泛型统配>
public interface GenericInterface<T>{
T updateSome(Integer id);
public abstract void addSome(T t);
}
此时写接口的实现类,可以:
- 在定义实现类时确定泛型的类型
public class GenericsImpl implements GenericsInterface<String> {
@Override
public void addSome(String s) {
System.out.println("定义实现类时确定泛型的类型");
}
}
- 始终不确定泛型的类型,交给创建对象时确定
public class GenericsImpl<T> implements GenericsInterface<T> {
@Override
public void addSome(T t) {
System.out.println("不确定泛型类型,交给创建对象时确定");
}
}
//在测试程序中
public class Test{
public static void main(String[] args){
GenericsImpl<Integer> obj = new GenericsImpl<>();
obj.addSome(9527);
}
}
5、未知通配符?
- ? - 表示不确定的java类型,常用于形参中
- T是一个确定的类型,常用于泛型类和泛型方法。
public class TestClass<T>{
private T t;
public void test(List<?> list){
}
}
-
?和 T,声明了T类型以后,T可以操作,而?不行
-
T只能用extends来限定缩小范围,而?可以使用extends和super
6、未知通配符的高级使用
//测试数据:
class Animal{}//父类
class Dog extends Animal{}//子类
class Cat extends Animal{}//子类
泛型的上限:
- 格式:<? extends 类A>
- 意义: 实例化时的类只能是类型A及其子类型
- 举例:
//接口中定义的抽象方法
List<? extends Animal> listSome();
//实现类中重写
@Override
public List<Animal> listSome(){
return null;
}
//再比如某方法的形参中:
public void doSome(List<? extends Animal> list){
}
//此时实例化时:
list = new ArrayList<Animal>(); //√
list = new ArrayList<Dog>(); //√
list = new ArrayList<Object>(); //×
- 注意点1:
上界通配符下add失效(只能add null)
,可以get
List<? extends Animal> list;
list = new ArrayList<Animal>();
list.add(new Animal()); //编译error
list.add(null); //√
list.get(0); //√
- 注意点2: 既然不能add,那么想加元素就得在实例化时就放进去
List<? extends Animal> list;
list = new ArrayList<Animal>(){
{
add(new Animal());
add(new Dog());
}
};
- 注意点3: 能add的元素的类型是实例化时指定的类型及其子类
List<? extends Animal> list;
list = new ArrayList<Dog>(){
{
add(new Animal()); //指定Dog后,编译error
add(new Dog()); //可以add的是Dog及其子类
}
};
泛型的下限:
-
格式:<?super 类A>
-
意义: 实例化时的类只能是类型A及其父类型
-
举例:
List<? super Animal> list ;
list = new ArrayList<Object>(); //√
list = new ArrayList<Animal>(); //√
list = new ArrayList<Dog>(); //×
- 注意点1: 添加对象时,在实例化时添加和实例化后添加,限制不同。
class SomeOne {}
class People extends SomeOne{}
class Animal extends SomeOne{}
实例化时添加对象,可以添加A类的父类的其他子类:
List<? super Animal> list = new ArrayList<SomeOne>(){
{
add(new Object()); //error,超过了Someone
add(new People()); //√ 是SomeOne的子类,但和Animal无关
add(new SomeOne()); //√
add(new Animal()); //√
add(new Dog()); //√
}
}
实例化后,恢复成只能add类A和其子类
list.add(new SomeOne()); //error
list.add(new People()); //error
add(new Animal()); //√
add(new Dog()); //√
- 注意点2: 上界通配符get出来的对象默认是Object类型,可根据需要强转
Object obj = list.get(0);
Animal a = (Animal)list.get(0);
后面有内容再补吧,?这个是真有毒。
add元素限制的问题,参考:【Java擦除】