Java中的泛型T

泛型是JDK 1.5的一项新特性,它的本质是参数化类型(Parameterized Type),即所操作的数据类型在定义时被指定为一个参数。当我们使用的时候给这个参数指定不同的对象类型,就可以处理不同的对象。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口和泛型方法。

一、泛型实现方式

泛型声明方式:<占位符>
占位符有两大类:

  1. 普通占位符,E, T, K, V一般是单字母大写,表示接收特定的类型。
  2. 通用占位符,也叫通配符,占位符中包含“?”问号,表示接任意数据类型或者指定范围数据类型。

泛型类

泛型类和普通类的区别就是类定义时,在类名后加上泛型声明。泛型类的内部成员、方法就可以使用声明的参数类型。泛型类最常见的用途就是作为容纳不同数据类型的容器类,比如Java集合容器类。

class GenericClass<T>{
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

泛型接口

和泛型类一样,泛型接口在接口名后添加泛型声明,接口方法就可以直接使用声明的参数类型。
实现类在实现泛型接口时需要指明具体的参数类型,不然默认类型是 Object,这就失去了泛型接口的意义。

interface genericInterface<K, V> {
    K getKey();
    V getValue();
}

未指明类型的实现类,默认是 Object 类型:

class GenericClass implements genericInterface{
    @Override
    public Object getKey() {
        return null;
    }

    @Override
    public Object getValue() {
        return null;
    }
}

指明了类型的实现:

class GenericClass implements genericInterface<String, Object> {
    @Override
    public String getKey() {
        return null;
    }

    @Override
    public Object getValue() {
        return null;
    }
}

泛型方法

泛型方法指的是使用泛型的方法。如果这个方法所在的类是个泛型类,直接使用类声明的参数类型。

如果这个方法所在的类不是泛型类或者他想要处理不同于泛型类所声明的参数类型,这时候我们就需要使用泛型方法。即在方法前加入泛型声明,方法就可以直接使用声明的参数类型。

class GenericClass {

    private Object value;

    public <T> T getValue(){
        return (T)value;
    }

    public void setValue(Object value){
        this.value = value;
    }
}

方法getValue()就是泛型方法,调用代码如下:

GenericClass genericClass = new GenericClass();
genericClass.setValue(123);

Integer value = genericClass.getValue();

二、泛型的优点

数据类型安全

泛型的主要目的通过参数类型检查,提高JAVA程序数据类型的安全,提早发现错误,避免ClassCastException 异常发生。

List list = new ArrayList();
list.add("test generic");
list.add(123);// 注:123会转换成Integer对象
list.add(null);

当不实用泛型的时候,上面代码的使用是被允许的,我们可以放入任何对象,当我们按照某种对象类型去取数据就会出现ClassCastException 异常。

//List<String> list = new ArrayList<String>();
List<String> list = new ArrayList();
list.add("test generic");
//list.add(123);
list.add(null);

List是泛型接口,当我们这样使用的时候,“123”这个元素是不被允许使用的。

消除类型转换

当我们不使用泛型时,每次取出集合中的元素都需要我们强制转换成我们需要的元素。(Object->Class Type)。

List list = new ArrayList();
list.add("test generic");
String s = (String) list.get(0);

“test generic”存入list是一个Object的对象,取出来是我们需要转换成我们要的字符串String类型。

List<String> list = new ArrayList();
list.add("test generic");
String s = list.get(0);

使用泛型后list.get(0)中取出来的数据类型,就是我们存入时的String类型。

简码,提高效率

通过泛型的应用,做到了简码,而且JVM中类文件也相应的减少。JVM几乎没有做任何的更改,所有的类型校验,类型转换都是在编译器阶段完成。

三、通配符

无限制通配符<?>

定义时不关心或者不确定实际要操作的数据是什么类型,可以使用无限制通配符(尖括号里一个问号,即 <?> ),表示可以持有任何类型。

class GenericClass<T> {
    private T data;
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
    @Override
    public String toString() {
        return "GenericClass{" +
                "data=" + data +
                '}';
    }
}

泛型类定义

public static void print(GenericClass<?> context){
     System.out.println(context);
}

通配符定义的方法参数类型

GenericClass<Integer> integerGenericClass = new GenericClass<>();
integerGenericClass.setData(123);
GenericClass<String> stringGenericClass = new GenericClass<>();
stringGenericClass.setData("123");
print(integerGenericClass);
print(stringGenericClass);

使用时?是一个万能的类型,T只能是具体的类型

上界通配符 <? extends E>

在类型参数中使用extends表示这个泛型中的参数必须是 E 或者 E 的子类,这样有两个好处:

  1. 如果传入的类型不是E或者E的子类,编辑不成功
  2. 泛型中可以使用E的方法,要不然还得强转成E才能使用
List<? extends Number> list = new ArrayList<Number>(){{add(new Integer(456));add(new Long(456L)); }};
list.add(null);
//list.add(new Integer(123));
//list.add(new Long(123L));
System.out.println(list.size());
Number number = list.get(0);

当使用上边界通配符时,是不允许往里存数据的,不确定具体元素类型。

下界通配符 < ? super E>

在类型参数中使用super表示这个泛型中的参数必须是E或者E的父类。

用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象。

List<? super C> list = new ArrayList<B>(){{add(new B()); add(new C());}};
list.add(null);
list.add(new C());
list.add(new B());
//list.add(new A());
Object object = list.get(0);

C是B的子类,B是A的子类。当使用下界通配符时,是允许存放E和E的子对象数据的,因为子类对象的引用可以赋值给父类对象的引用(顶级父类是Object),所以取出来的数据类型是Object.

注意

<? extends Number><? super C> 只能在参数中起限制作用,不可以对其进行添加或赋值操作但是可以使用get

例如:

List<? extends Animal> animals = new ArrayList<>();
animals.add(new Animal()); // 错误,无法添加 Animal 类的实例
animals.add(new Dog()); // 错误,无法添加 Dog 类的实例
animals.add(new Cat()); // 错误,无法添加 Cat 类的实例
Animal animal = animals.get(0); // 可以获取元素并将其赋值给 Animal 类型的变量

四、总结

  • 在实例化的时候,就必须声明泛型具体是一个什么类型。
  • <? extends Number>和<? super C> 只能在参数中起限制作用,不可以对其进行添加或赋值操作但是可以使用get
  • 使用泛型、通配符提高了代码的复用性。
  • 把一个对象分为声明、使用两部分的话。泛型侧重于类型的声明上代码复用,通配符则侧重于使用上的代码复用。泛型用于定义内部数据类型的不确定性,通配符则用于定义使用的对象类型不确定性。
  • 如果类型参数在方法声明中只出现一次,可以用通配符代替它。
  • 不能同时声明泛型通配符上界和下界。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值