Java泛型

泛型概述

Java泛型是JavaSE1.5新加入的机制,它将Java面向对象的特点进一步强化。

Java中每一个对象都属于一个类型,类可以继承,父类是子类的共性抽取,在类继承基础上出现了抽象类,抽象类是其子类的抽象,子类必须实现抽象类定义的方法,为了解决单继承问题以及面向“能力”编程思想,又出现了接口,接口定义了一些“能力”,实现它的类都将具有这些“能力”,因此接口是对抽象类的又一层深化。(另外还有注解,可以按需向类加装想要的功能和属性。)而泛型就是广泛的类型——类,抽象类,接口。泛型可以和它们一样充当类的属性,方法的参数或者返回类型。通过对类型的“泛化”,同一套代码可以适配多种类型,可以复用代码,提高灵活性,降低耦合。

最简单的例子:

public class Pair<T> {

    T first;
    T second;

    public Pair(T first, T second){
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }

    public T getSecond() {
        return second;
    }
}

T就是泛型的标记,Pair类可以适配任意类,它的属性first和second都是T类型的。

泛型还可以作为方法传入参数类型:

public static <T> int indexOf(T[] arr, T elm){
    for(int i=0; i<arr.length; i++){
        if(arr[i].equals(elm)){
            return i;
        }
    }
    return -1;
}

在返回类型前加泛型标记,就可以在后面的传入参数中使用泛型了。

泛型擦除

泛型作为在JDK1.5后才加入的新成员,它的功能并未被最初的Java虚拟机所支持,在1.5之前,类似的泛化功能是通过Object类和强制类型转换实现的。在1.5之后,为了兼容,也没有在虚拟机中新加入泛型的实体,而是在编译阶段将.java文件编译成字节码时,泛型转化成对应的原生类型(raw type),这种机制叫做“泛型擦除”。因此Java泛型被称为“伪泛型”(C#是有真泛型)

泛型的限制

由于Java泛型是伪泛型,所以存在一些限制,这些限制有时会反直觉,比较难懂,但在实际编程中借助IDE还是可以顺利编写的。因此这部分内容作为了解。

1,基本数据类型不能用于实例化参数类型:

Pair<int> minmax = new Pair<int>(1,100);

以上代码不合法,解决方法就是使用基本类型对应的包装类。

2,运行时类型信息不适用于泛型

因为是伪泛型,所以泛型类没有自己的xxx.class类型信息。

Pair<Integer> p1 = new Pair<Integer>(1,100);
Pair<String> p2 = new Pair<String>("hello","world");
System.out.println(Pair.class==p1.getClass());
System.out.println(Pair.class==p2.getClass());

都会返回true

3,当泛型擦除遭遇重载

由于在编译阶段,泛型会被擦除变成Object,可能导致重载的方法在变成字节码后变成一样的了引发冲突。

例如:

public GenericClass{

    public static void method(List<String> list){
        System.out.println("invoke method(List<String> list)");
    }

    public static void method(List<Integer> list){
        System.out.println("invoke method(List<Integer> list)")
    }
}

上面的代码是不能被编译的,因为一旦编译两个方法将会变成一样,List和List都将会擦除变为原生类型List。擦除动作导致方法的特征签名变得一样,这在Java中是不被允许的。

可选的解决方式是将函数的返回值类型改变,是两者可以共存在一个.class文件中。但显然这是不优雅的。

4,不能通过类型参数创建对象

不合法语句:

T elm = new T();
T[] arr = new T[10];

5,泛型类类型参数不能用于静态变量和方法

很好理解静态变量和方法是多实例共享的,如果被多实例赋予不同的类型,会造成混乱。

6,多个类型限定

之前介绍类型参数限定的时候,我们介绍,上界可以为某个类、某个接口或者其他类型参数,但上界都是只有一个,Java中还支持多个上界,多个上界之间以&分隔,类似这样:

T extends Base & Comparable & Serializable

Base为上界类,Comparable和Serializable为上界接口,如果有上界类,类应该放在第一个,类型擦除时,会用第一个上界替换。

7,Java禁止泛型数组

如果可以使用泛型数组,就留有了将数组中元素改变成其他其他参数类型的元素的风险,会导致读写出错,因此为了杜绝这种不易察觉的奉献,Java直接禁用了泛型数组。

例如,下面的代码,会产生问题。

Pair<Object,Integer>[] options = new Pair<Object,Integer>[3];
Object[] objs = options;
objs[0] = new Pair<Double,String>(12.34,"hello");

因此要存放多个泛型,好的方式是使用容器类或者使用原生类型数组:

Pair[] options = new Pair[]{
      new Pair<String,Integer>("1元",7),
      new Pair<String,Integer>("2元", 2),
      new Pair<String,Integer>("10元", 1)};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值