java泛型基本示例

1.什么是java泛型

      Java泛型是j2SE 1.5中引入的一个新特性,即参数化类型,通俗来说就是给定一个所操作的参数类型,这种类型可以用在类、接口和方法的创建中,也称作泛型类、泛型接口、泛型方法。


2.为什么要用java泛型

使用泛型的好处是能够带来以下的几点好处:

  1. 泛型具有泛化能力
          通常来说,当我们使用类,接口或者方法时,如果将某一种类型硬编码到程序中 ,那么该程序只支持这一种类型,而当有新的参数类型时,就需要重写支持该类型相同 的类、接口或者方法。很明显这样不利于程序的扩展性和复用性。
          基于以上问题,我们可能会想到,在使用java多态过程中,我们可以将类型进行向上转型(java所有类型父类都是Object),这样就可以对方法进行复用了,当我们可能需要对其进行强制类型转换,这种方式可能会引起装箱和拆箱,造成性能的开销,并且这些还是需要人为进行操作的,因此有时候不能完全避免出现类型转换异常等问题,而这些问题只有在程序运行时才能发现。

  2. 泛型能够类型安全
          使用泛型就能够提高java程序的类型安全性,在程序的编译阶段,编译器能够进行类型检查,进而可以在更高程度上验证类型假设,当检查不通过,程序无法继续运行,这样显示的给编程人员做出提示,避免在程序运行时出现类型转换异常(ClassCast Exception)的情况。

  3. 泛型能够消除强制类型转换
          泛型可以消除源代码中的许多强制类型转换,代码里面不需要对指定某一种类型,我们通常用(T)来表示表示类型即可,这样提高了代码的可扩展性,也可以使代码更加可读,并减少出错的机会。

2.1 java泛型擦除

      实际上,当使用泛型时,编译器会对类型进行检查,检查通过后,之后会对类型进行擦除(即所有类型参数都用他们的限定类型替换,包括类、变量和方法)除结构化信息外的所有东西,(结构化信息指与类结构相关的信息,而不是与程序执行流程有关的,即与类及其字段和方法的类型参数相关的元数据都会被保留下来通过反射获取到)。如果未指定限定类型,则会使用他们共同的父类(一般为Object)做为类型的上边界即限定类型。因此注意泛型不能够显示地运用到运行时类型的操作中(如向上向下转型、instanceof 操作、直接实例化类型参数等),因为类型的擦除,在运行时这些类型已经丢失了(如需要,我们可以通过反射对泛型对象进行实例化)
      泛型的擦除并不是将全部信息进行擦除,它会在字节码里指令集之外的地方保留部分泛型信息,泛型接口、类、方法定义上的所有泛型、成员变量声明处的泛型都会被保留类型信息,java泛型机制虽然在编译期间进行了擦除,但是有些泛型相关的信息被保存到class字节码的常量池里面,泛型的代码会生成一个signature签名字段,这个签名字段指明了这个常量池的地址。


3.java泛型的常用方式

3.1泛型类、方法

//泛型类
class  Test<T> {

    //一般泛型方法
    public void getValue(T per){
        System.out.println(per);
    }

    //静态泛型方法操作类型不确定,需要指定<T>
    public  static <T> void getValueByStatic(T per){
        System.out.println(per);
    }

    //静态泛型方法,指定类型边界,防止类型擦除造成编译无法通过的情况
    public static <T extends Comparable> int get1(T o1,T o2){
        return o1.compareTo(o2);
    }
}

      泛型类的通常使用方式,如果需要限定上边界即Test<T extends 上边界类型>,值得注意的是Java类的类型参数限定只有extends形式,没有super形式,因此Test<T super 下边界类型>并不合法。

3.2 java泛型通配符

      java中有两种类型的通配符,一种是限定通配符,通常表示为 <? extends T> 或<? super T>,<? extends T>告诉类型都是T的子类,且T为他们的上边界,<? super T>告诉类型是T的父类,且T为他们的下边界。另一种是非限定通配符,通常表示为<?>,它可以用任意泛型类型来替代。
图1
      如图1,泛型擦除前的类型检查是针对引用的,泛型中参数化类型无法支持继承关系。
图2
      我们可以观察到,这两种情况都能正确编译且输出结果,而图1指定ArrayList为Object为具体泛型类型却无法编译通过。实际上list6是一个未知类型,我们可以把任意类型的赋值给未知类型,而具体类型告知了编译器,然后编译器会进行安全检查,因此list4无法通过。List8是一个原型类型,它在编译期间不会进行安全检查。

3.3 泛型通配符代码示例

package Genericity;


import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class secondGenericity {

    public static void main(String[] args){
        //泛型为Integer类型
        List<Integer> int_list = new ArrayList<>();
        int_list.add(1);
        int_list.add(2);
        //泛型为String类型
        List<String> str_list = new ArrayList<>();
        str_list.add("a");
        str_list.add("b");
        //泛型为Float类型
        List<Float> decimal_list = new ArrayList<>();
        decimal_list.add(11.11F);
        decimal_list.add(12.11F);
        //打印list(泛型限定为List<? extends Number>)
        printList1(int_list);
        printList1(str_list); //编译错误,由于泛型限定,String类型并不是Number的子类,也非其上边界
        printList1(decimal_list);
        //打印list(泛型限定为?,?为非限定通配符可接收任意类型泛型)
        printList2(int_list);
        printList2(str_list);
        printList2(decimal_list);

        //定义泛型为Object类型
        List<Object> obj_list = new ArrayList<>();
        obj_list.add(123);
        obj_list.add("abc");
        printList3(int_list);
        printList3(str_list); //编译错误,非Integer的父类型
        printList3(decimal_list);//编译错误,非Integer的父类型
        printList3(obj_list);


    }

    private static void printList1(List<? extends Number> list) {
        // list.add(1); 非法添加
        for(Object ls:list){
            System.out.println(ls);
        }

    }
    //<T extends E> 用于定义类型参数,声明了一个类型参数 T,这个具体类型是未知的,只知道它是 E 或 E 的某个子类型,此方法与printList1相同效果
    private <T extends Number> void printList11(List<T> list){
        for(Object ls:list){
            System.out.println(ls);
        }
    }

    private static void printList2(List<?> list) {
        for(Object ls:list){
            System.out.println(ls);
        }
    }

    private static void printList3(List<? super Integer> list){
        // list.add(1); 可以添加此元素
        for(Object ls:list){
            System.out.println(ls);
        }
    }
}
 值得注意的是,上面这些打印函数里面,无法对list进行添加操作,因为无法值得list的具体类型,为保证类型安全性,不允许进行写入,而<? super T>表示为T的父类型,因此可以写入,如在printList1和printList3中同时添加list.add(1);
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值