Java泛型之Java中的E、T、?讲解配套源代码

🍁 先说结论:

  • T、E、K、V、?本质都是通配符。用于定义泛型类、泛型方法、泛型接口…换成其他字母也行,只是这几个字母是一种编码约定。
  • T,即type,表示一个具体的Java类型
  • E,即element,常用于集合,如List、Set
  • 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类型
  • 泛型方法可以声明为 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擦除】

转载地址:https://blog.csdn.net/llg___/article/details/130026077

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值