泛型

作者: 覆水无言 | 来源:发表于2018-12-26 18:20 被阅读0次

    泛型概述

    1:什么是泛型?:

    泛型:“参数化类型”,可以从字面理解,参数化,在我们用参数中,定义方法用形参,调用传递实参。类型
    在Java中就是指这个类属于那个类。而参数化类型,顾名思义,就是就是将类型定义为参数(可以称之为类型
    形参),然后在调用时传入具体类型(类型实参),在你编写一个泛型类时,就可以用在多处。

    2:泛型本质:

    本质就是参数化类型,也就是在编写代码是不指定具体参数数据的类型,而在使用中指定一个参数,来确定
    编写的代码在处理什么类型的数据。这种参数类型可以用在类,接口和方法中,分别被称为泛型类,泛型接口
    泛型方法。

    3:好处

    泛型程序设计:意味着编写的代码可以被很多不同类型的对象所重用。
    好的错误检查,让错误在编译期间就暴露出来。

    举例:

    List arrayList = new ArrayList();
    arraylist.add("tian");
    arraylist.add(100);
    for(int i = 0; i< arraylist.size(); i++){
        String s = (String)arraylist.get(i);
    }
    

    上述代码为Java老版本的代码,程序运行时毫无疑问会报错,因为之前的Java list没有对数据类型做限制,它的操作
    都是在Object上的,所以一个list结合可以放所有类型的对象,这就造成了很严重的问题,
    在Java1.5之后Java团队推出了Java新的能力,泛型,很好的解决了这个问题,之后的代码就变成了这样

    List<String> arraylist = new Arraylist<>();
    arraylist.add("tian");
    arraylist.add(100); //1
    

    在上述代码中,注释1出会直接报错,无法编译,给list集合做了很好的限制,这就是我们需要的泛型。

    实例化泛型类型

    以List为例,List的源码为泛型接口的定义,
    泛型实例化为确定泛型中参数化类型的类型:eg:List<String>
    String就实例化了泛型类型。
    从上面看出:泛型类有点像普通类的工厂。

    泛型通用类

    /**
    * Box:是一个通用类,
    * T:传递给泛型类的类型化参数,他可以采用任何类
    * t: 泛型类型T的实例。
    */
    public class Box<T> {
        private T t;
    }
    

    泛型类型命名约定

    上例中T就是泛型的类型化参数,一般可以使用大写字母表示,在实际开发中普遍的命名约定为,
    E-element:主要用于Java collentions框架使用。
    K-key:主要用来表示地图的参数类型,或map等的K.
    V-value:主要表示地图参数和map等的值,可K一起用
    N-number:主要表示数字。
    T-type:表示一个类中的第一个泛型
    S,U,V,和T一样不过分别表示第二,第三,第四个泛型。

    特性

    泛型只会在编译时有效

    List<String> strings = new ArrayList<>();
    List<Integer> integers = neww ArrayList<>();
    if (strings.getClass().equals(integers.getClass())){
        System.out.println("泛型测试,类型相同");
    }
    //这里会输出测试日志,
    

    上面的例子可以测试出,Java泛型在编译后会是我们平常看的类,没有泛型标志,也就是说Java泛型只会在编译
    阶段有效,编译过程中,正确检查泛型后,会将泛型的相关信息擦除,也就是说泛型不会进入运行阶段,
    也就是说,泛型会在逻辑上让人觉得它有许多不同的类型,但实际上都是相同的基本的编码类。

    泛型的三种适用方式:泛型类、泛型接口、泛型方法

    泛型类:

    泛型类就是上述泛型通用类的代码,

    /**
    * {class 类名 <泛型类型标识>{ 类主体}}
    * Box:是一个通用类,
    * T:传递给泛型类的类型化参数,他可以采用任何类
    * t: 泛型类型T的实例。
    */
    public class Box<T> {
        private T t;
    }
    

    以T代表泛型类的类型形参,你可以将T看成一个类,这个类是什么类就看你在使用的时候传入的实参了,这个实参
    会起到限制作用,只有符合这个实参的操作在会被允许。在不传入实参的情况下这个形参可以是任何类型。

    泛型方法

    [作用域标识符] <泛型类型标识> [返回类型] 方法名(参数){}

    public <T> T getName(T t){
        return t;
    }
    //当调用泛型方法时,在方法名钱的<>中放入具体的类型
    String s = class.<String>getName("song");
    

    泛型接口

    public interface DataListener<T>{
        T next();
    }
    

    泛型接口定义给出T泛型参数,在实现接口时会有两种情况
    1:不指定泛型参数的具体类型,这时需要实现类是泛型类

    public class DataListenerImpl<T> implements DataListener<T>{
        @Override
        public T next();
    }
    

    2:实现泛型接口时直接指定泛型参数的具体类型,这个可以实现普通类

    public class DataListenerImpl implements DataListener<String>{
        @Override
        public String next(){}
    }
    

    泛型上下边界

    当我们使用泛型时,希望定义的泛型不是被所有的类使用,而是特定的一些类使用时,我们指定泛型的具体类型
    这样就违背了泛型的理念,所以泛型还有上下边界的问题,这个上下边界就是限定这个泛型可以被那些类使用。

    上下边界的使用

    泛型定义时:

    <T>:用于定义泛型,类型为未知,类型没有限制.
    <T extends classA & interfaceB..>:声明有边界的泛型, 泛型T继承classA,实现接口B。
    extends在这里不代表着继承,而是限制的意思,多个参数使用&分割,如果限定中有类,则
    类必须放到一个

    泛型实例化时(通配符限定)

    <?>:标识通配符,用于标识泛型实例化是的类型。
    <? extends 父类型>:泛型上边界,用于表示实例化时可以确定的父类型的类型。
    <? super 子类型> :泛型下边界,用于表示实例化时可以确定的子类型的类型。

    public void keyValue(Apple<? extends Number> obj) {
        //确定Apple内的泛型为继承了Number类的类型。
        System.out.println(" "  + obj.get()) ;
    }
    

    泛型擦除

    类型擦除就是说Java泛型只能用于在编译期间的静态类型检查,然后编译器生成的代码会擦除相应的类型信息
    这样在运行中jvm根本不知道泛型所代表的具体类。因为泛型是1.5版本后引入的,为了保持向下兼容,才有的
    类型擦除。

    public class Node<T>{
        private T data;
    }
    //编译中通过了类型检查,编译完成后的代码实际是这样的
    public class Node{
        private Object data;
    }
    

    这意味着不管我们声明的Node泛型是什么类型,到运行期间都会变成object,

    public class Node<T extends Number>{
        private T data;
    }
    //编译后会是
    public class Node{
        private Number data;
    }
    

    泛型的限制

    由于泛型类的擦除,会存在一些适用限制。

    泛型类不能使用基本类型。

    基本类型会使泛型编译报错。

    泛型不允许进行直接实例化

    Java泛型不允许进行静态化

    静态变了在类中共享,需要确定的类型,因此编译器无法确定要使用的类型,
    所以不允许进行静态化。

    class StaticSample<T>{
        private static T t; //编译前类型检查报错
        public static T getT(){ //编译前类型检查报错
            return t;
        }
    }
    

    Java泛型不允许直接进行类型转换(通配符可以)

    1:以定下泛型类型的变量不能直接进行类型转换。

    List<Integer> integers = new ArrayList<Integer>();
    List<Double> doubles = new ArrayList<Double>();
    integers = doubles; //这个在编译过程中会报错,类型转换出现异常
    

    2:通配符是可以进行转换的

    static void cast(List<?> orgin, List<?> dest){
        dest = orgin; //这样是可以进行转换的。
    }
    

    Java泛型不允许直接适用instanceof运算符进行运行时类型检查

    List<String> strings = new ArrayList<String>();
    //这个类型检查是错误的,无法实现。
    System.out.println(strings instanceof ArrayList<Double>);
    

    Java泛型不允许创建确切类型的泛型数组

    List<Integer>[] list = new ArrayList<Integer>[2];
    

    java泛型不允许作为参数进行重载

    因为泛型的擦除,擦除后参数化类就会变的一样,所以无法进行方法重载

    public class Test<T>{
        public void test(List<Integer> list){}
        public void test(LIst<Double> list){}
        //这个编译后list会变成一样的,所以不能进行方法重载。
    }
    

    相关文章

      网友评论

          本文标题:泛型

          本文链接:https://www.haomeiwen.com/subject/zdgflqtx.html