zoukankan      html  css  js  c++  java
  • 关于Java泛型深入理解小总结

    1、何为泛型

    首先泛型的本质便是类型参数化,通俗的说就是用一个变量来表示类型,这个类型可以是String,Integer等等不确定,表明可接受的类型,原理类似如下代码

    int pattern; //声明一个变量未赋值,pattern可以看作是泛型
    pattern = 4;
    pattern = 5;//4和5就可以看作是String和Integer

    泛型的具体形式见泛型类、泛型方法

      *泛型类形式如下

    class Test<T>
    {
        private T t;
        Test(T t)
        {
            this.t = t;
        }
        public  T getT()
        {
            return t;
        }
    
        public void setT(T t)
        {
            this.t = t;
        }
    }

      *泛型方法举例代码如下

    public <T> void show()
    {
        operation about T...
    }

    泛型参数类型声明必须在返回类型之前

    2、为何要引入泛型,即泛型与Object的优势

    由于泛型可以接受多个参数,而Object经过强制类型转换可以转换为任何类型,既然二者都具有相同的作用,为何还要引进泛型呢?

    解答:泛型可以把使用Object的错误提前到编译后,而不是运行后,提升安全性。以下用带泛型的ArrayList和不带泛型的Arraylist举例说明

    代码1:

    ArrayList al = new ArrayList();
    al.add("hello");
    al.add(4);//自动装箱
    String s1 = (String)al.get(0);
    String s2 = (String)al.get(1);//在编译时没问题,但在运行时出现问题

    首先声明无泛型的ArrayList时,其默认的原始类型是Object数组,既然为Object类型,就可以接受任意数据的赋值,因此编译时没有问题,但是在运行时,Integer强转成String,肯定会出现ClassCastException.因此泛型的引入增强了安全性,把类转换异常提前到了编译时期。

    3、类型擦除和原始类型

      *类型擦除的由来

    在JAVA的虚拟机中并不存在泛型,泛型只是为了完善java体系,增加程序员编程的便捷性以及安全性而创建的一种机制,在JAVA虚拟机中对应泛型的都是确定的类型,在编写泛型代码后,java虚拟中会把这些泛型参数类型都擦除,用相应的确定类型来代替,代替的这一动作叫做类型擦除,而用于替代的类型称为原始类型,在类型擦除过程中,一般使用第一个限定的类型来替换,若无限定则使用Object.

      *对泛型类的翻译

    泛型类(不带泛型限定)代码:

    class Test<T>
    {
        private T t;
        public void show(T t)
        {
    
        }
    }

    虚拟机进行翻译后的原始类型:

    class Test
    {
        private Object t;
        public void show(Object t)
        {
            
        }
    }

    泛型类(带泛型限定)代码: 

    class Test<? extends Comparable>
    {
        private T t;
        public void show(T t)
        {
    
        }
    }

    虚拟机进行翻译后的原始类型:

    class Test
    {
        private Comparable t;
        public void show(Comparable t)
        {
            
        }
    }

     *泛型方法的翻译

    class Test<T>
    {
        private T t;
        public void show(T t)
        {
    
        }
    }
    
    class TestDemo extends Test<String>
    {
        private String t;
        public void show(String t)
        {
            
        }
    }

    由于TestDemo继承Test<String>,但是Test在类型擦除后还有一个public void Show(Object t),这和那个show(String t)出现重载,但是本意却是没有show(Object t)的,

    因此在虚拟机翻译泛型方法中,引入了桥方法,及在类型擦除后的show(Object t)中调用另一个方法,代码如下:

    public void show(Object t)
    {
        show((String) t);
    }

    4、泛型限定

      *泛型限定是通过?(通配符)来实现的,表示可以接受任意类型,那一定有人有疑问,那?和T(二者单独使用时)有啥区别了,其实区别也不是很大,仅仅在对参数类型的使用上。

    例如:

    public void print(ArrayList<?> al)
    {
        Iterator<?> it = al.iterator();
        while(it.hasNext())
        {
            System.out.println(in.next());
        }
    
    }

    public
    <T> void print(ArrayList<T> al) { Iterator<T> it = al.iterator(); while(it.hasNext()) { T t = it.next(); //区别就在此处,T可以作为类型来使用,而?仅能作为接收任意类型 System.out.println(t); } }

      *? extends SomeClass  这种限定,说明的是只能接收SomeClass及其子类类型,所谓的“上限”

      *? super SomeClass 这种限定,说明只能接收SomeClass及其父类类型,所谓的“下限”

    一下举例? extends SomeClass说明一下这类限定的一种应用方式

    由于泛型参数类型可以表示任意类型的类类型,若T要引用compareTo方法,如何保证在T类中定义了compareTo方法呢?利用如下代码:

    public <T extends Comparable> shwo(T a, T b)
    {
        int num = a.compareTo(b);
    }

    此处用于限定T类型继承自Comparable,因为T类型可以调用compareTo方法。

      *可以有多个类型限定,例如:

    <T extends Comparable & Serializable>

    这种书写方式为何把comparable写在前边?因为由于类型擦除的问题,原始类型是由Comparable替换的,因此写在前边的是类中存在此类型的泛型方法放在前边,避免调用方法时类型的强制转换,提高效率。

    class Test<T extends Comparable & Serializable>
    {
        private T lower;
        private T upper;
    
        public Test(T first, T second) //此处是利用Comparable的方法,因此把Comparable写在前边,类型擦除后为Comparable,若为Serializable,还得用强制类型转换,否则不能使用compareTo方法。
        {
            int a = first.compareTo(second);
            ...
        }
    }

      *关于泛型类型限定的“继承”误区

    总有些人误把类型的限定当作继承,比如:

    //类型是这样的
    <Student extends Person>
    //然后出现此类错误
    ArrayList<Person> al = new ArrayList<Student>();

    此处的<Person>, <Student>作为泛型的意思是ArrayList容器的接收类型,用一个简单的例子来说明

    ArrayList是一个大型养殖场,<Person>表明的是他能够接收动物,而上边的new语句生成的是一个只能够接收猪的养殖场(ArrayList<Student>),即把一个大型养殖场建造成了一个养猪场,若是继承的大型养殖场肯定是还能接受狗、羊....的,但是现在建造成了养猪场,那还能接受别的动物么?所以这肯定是错误的用法!简而言之,泛型new时两边的类型参数必须一致。

    5、泛型的一些基本规则约束

      *泛型的类型参数必须为类的引用,不能用基本类型(int, short, long, byte, float, double, char, boolean)

      *泛型是类型的参数化,在使用时可以用作不同类型(此处在说泛型类时会详细说明)

      *泛型的类型参数可以有多个,代码举例如下:

    public <T, E> void show()
    {    
        coding operation.....    
    }                

      *泛型可以使用extends, super, ?(通配符)来对类型参数进行限定

      *由于类型擦除,运行时类型查询只适用于原始类型,比如instanceof、getClass()、强制类型转换,a instanceof (Pair<Employe>),在类型擦除后便是 a instanceof Pair,因此以上运行的一些操作在虚拟机中操作都是对原始类型进行操作,无论写的多么虚幻,都逃不出类型擦除,因为在虚拟机种并不存在泛型。

      *不能创建参数化类型的数组

    例如写如下代码:

    Pair<String>[] table = new Pair<String>[10]; //ERROR
    //让Object[] t指向table
    Object[] t = table;
    //向t中添加对象
    t[0] = new Pair<Employe>();
    //关键错误之处
    String s = table[0];

    由于Object可以接收任何类型,在里边存入 new Pari<Employe>时,没有任何问题,但是当取出的时候会出现ClassCastException,因此不能创建参数化类型数组。

      *不能实例化类型变量,及不能出现以下的类似代码

    T t = new T();
    //
    T.Class

    因为在类型擦除后,便是Object t = new Object();与用意不符合,即本意肯定不是要调用Object.

      *不能再静态域或方法中出现参数类型

    例如如下错误代码

    class Test<T>
    {
        private static T example;  //error
        public static void showExample() //error
        {
            action about T...
        }
        public static T showExample() //error
        {
            action about T....    
         }
    }    

    首先方法是一个返回类型为T的普通方法,而非泛型方法,这和在静态方法中使用非静态参数是一样的,静态方法是先于对象而存在内存中的,因此在编译的时候,T的类型无法确定,一定会出现“Cannot make a static reference to a non_static reference”这样类似的错误。

    但是这样的代码就是正确的

    class Test<T>
    {public static <T> T show()
        {
            action
        }
    }

    因为此处的静态方法是泛型方法,可以使用.

      *不能抛出或捕获泛型类的实例

        +不能抛出不能捕获泛型类对象

        +泛型类不能扩展Throwable,注意是类不能继承Throwable,类型参数的限定还是可以的。

        +catch子句不能使用类型变量,如下代码

    try
    {
        ....
    }
    catch(T e) //error
    {
        ...
    }

      *类型擦除后的冲突注意

    例如:

    class Pair<T>
    {
        public boolean equals(T value) //error
        {
            ....
        }
    }

    此处的错误的原因不能存在同一个方法,在类型擦除后,Pair的方法为,public boolean equals(Object value),这与从Object.class中继承下来的equals(Object obj)冲突。

      *一个类不能成为两个接口类型的子类,而这两个接口是同一接口的不同参数化。

    例如:

    class Calendar implements coparable<Calendar>{}
    
    class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>{} //error

    当类型擦除后,Calendar实现的是Comparable,而GregorianCalendar继承了Calendar,又去实现Comparable,必然出错!

    ———————————————————————————————————————————————————————————————————————————————

     先总结到此处。

  • 相关阅读:
    easy ui 表单ajax和from两种提交数据方法
    easy ui 下拉级联效果 ,下拉框绑定数据select控件
    easy ui 下拉框绑定数据select控件
    easy ui 异步上传文件,跨域
    easy ui 菜单和按钮(Menu and Button)
    HTTP 错误 404.3
    EXTJS4.2 后台管理菜单栏
    HTML 背景图片自适应
    easy ui 表单元素input控件后面加说明(红色)
    EXTJS 4.2 添加滚动条
  • 原文地址:https://www.cnblogs.com/fantasy01/p/3963593.html
Copyright © 2011-2022 走看看