博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
认识Java泛型
阅读量:6256 次
发布时间:2019-06-22

本文共 5652 字,大约阅读时间需要 18 分钟。

一、泛型的概念

泛型实现了参数化类型的概念,使代码可以用于多种类型

二、泛型的目的

  1. 希望类和方法能够具备最广泛的表达能力
  2. 用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性

三、泛型的使用

  1. 普通泛型类

    public class NormalGenericsClass
    { private T key; public Generics(T key){ this.key = key; } public T getKey() { return key; } public void setKey(T key) { this.key = key; }}// 泛型类实例NormalGenericsClass
    a = new NormalGenericsClass
    (0);// Java7 开始支持省略后面的参数类型NormalGenericsClass
    a = new NormalGenericsClass<>(0);
  2. 普通泛型接口

    // Java中的 Comparator接口public interface Comparator
    { int compare(T o1, T o2); boolean equals(Object obj);}// 接口实现例子 java.text.Collatorpublic abstract class Collator implements java.util.Comparator
    , Cloneable{ //...主体代码}
  3. 普通泛型方法

    private 
    int normalGenericsMethod(Generics
    i){ //
    表示声明 T为泛型,写在返回值类型之前// 也可以声明多个泛型,如:
    return 0;}// 这是错误的,会提示 Cannot resolve symbol 'T'/*private int normalGenericsMethod(Generics
    i){ return 0;}*/

    有上界的泛型:

    上界:通过关键字extends来表示给定的上界,那么参数就必须是给定的上界或者其子类型。上界可以是某个具体的类或接口,也可以是其他参数

  4. 上界为具体的类

    public class GenericsUpperBound
    extends NormalGenericsClass
    { public GenericsUpperBound(T key){ super(key); }}
  5. 上界为具体的接口

    public 
    T compareWith(T[] arr){ T start = arr[0]; for(int i = 1; i < arr.length; i++){ System.out.print(start.equals(arr[i])); } return start;}
  6. 上界为其他类型参数

    public class OtherUpperBound
    { public
    void otherArgs(NormalGenericsClass
    a){ // E是OtherUpperBound的类型参数,T是otherArgs方法的类型参数 // T的上界限定为E }}//例子:OtherUpperBound
    a = new OtherUpperBound<>;OtherUpperBound
    b = new OtherUpperBound<>;a.otherArgs(b);

四、通配符

  1. 有限定通配符

    //重写6中的方法public void otherArgs(NormalGenericsClass
    a){ }
    取自《Java编程的逻辑》8.2
    <T extends E> 和 <? extends E>的区别
    ①<T extends E>:用于定义类型参数,它声明了一个类型参数T,可放在泛型类定义中类名后面、泛型方法返回值前面
    ②<? extends E>:用于实例化类型参数,它用于实例化泛型变量中的类型参数,只是这个类型是未知的,只知道是E或E的某个子类型
  2. 无限定通配符

    public int demo(OtherUpperBound
    a){ }// 这两个等效public
    int demo(OtherUpperBound
    a){ }

    通配符重要限制: 只能读,不能写

    取自《Java编程的逻辑》8.2
    总结:
    1) 通配符形式都可以用类型参数的形式来替代,通配符能做的,用类型参数都能做
    2) 通配符形式可以减少类型参数,形式上往往更为简单,可读性也更好,所以,能用通配符的就用通配符
    3) 如果类型参数之间有依赖关系,或者返回值依赖类型参数,或者需要写操作,则只能用类型参数
    4) 通配符形式和类型参数往往配合使用,定义必要的类型参数,使用通配符表达依赖,并接受更广泛的数据类型
  3. 超类型通配符(无法用类型参数替代)

    public int demo(OtherUpperBound
    a){ }
    取自《Java编程的逻辑》8.2
    总结:
    1)通配符的目的是为了使方法接口更为灵活,可以接受更为广泛的类型
    2)<? super E>用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象,它不能被类型参数形式替代
    3)<?>和<? extends E>用于灵活读取,使得方法可以读取E或E的任意子类型的容器对象,它们可以用类型参数的形式替代,但通配符形式更为简洁

五、泛型的局限性

  1. 父类实现了一个泛型接口,子类希望自定义泛型接口中的方法,只能重写父类的实现

    class Base implements Comparableclass Child extends Base// 希望重写Comparable的比较方法/*    class Child extends Base implements Comparable
    // 错误,因为类型擦除的原因,实际实现的都是Comparable接口,接口不允许被实现两次*/// 正确重写Comparable的比较方法的方式class Child extends Base { @Override public int compareTo(Base o){ if(!(o instanceof Child){ throw new IllegalArgumentException(); } Child c = (Child)o; \\实现代码 return 0; } \\其他代码}
  2. 类型参数不能作为静态变量和静态方法的类型

    Class Normal
    { public static demo1(T param){ // 错误的方法 } public static
    void demo2(E param){ // 正确的方法 }}
  3. 不能通过类型参数创建对象

    T a = new T(); // error Type parameter 'T' cannot be instantiated directly

六、泛型的使用细节

Java中的泛型是通过类型擦除实现的,类型参数在编译时会被替换为Object

  1. 对于不同传入的类型实参,生成的相应对象实例一样

    Generics
    demo = new Generics<>(123);Generics
    demo2 = new Generics<>("test");System.out.println(demo.getClass() == demo2.getClass()); // true
  2. 静态方法和泛型

    静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法

    /**  如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)  即使静态方法要使用泛型类中已经声明过的泛型也不可以。  如:public static void show(T t){..},此时编译器会提示错误信息     "StaticGenerator cannot be refrenced from static context"*/public static 
    void show(T t){}
  3. 泛型的上下边界添加,必须与泛型的声明一起

    //在泛型方法中添加上下边界限制的时候,必须在权限声明与返回值之间的
    上添加上下边界, //即在泛型声明的时候添加//public
    T showKeyName(Generic
    container)编译器会报错:"Unexpected bound"public
    T showKeyName(Generic
    container){ System.out.println("container key :" + container.getKey()); T test = container.getKey(); return test;}
  4. 基本类型不能用于实例化类型参数
    泛型要求能包容的是对象类型,基本类型在java中不属于对象
  5. 运行时类型查询只适用于原始类型

    NormalGenericsClass
    a = new NormalGenericsClass<>(0);a instanceof NormalGenericsClass
    ;// Error Illegal generic type for instanceofa instanceof NormalGenericsClass
    ;// Error Cannot resolve symbol 'T'a instanceof NormalGenericsClass; // Passa instanceof NormalGenericsClass
    ; // Passa.getClass(); // class com.example.demo.generics.NormalGenericsClass
  6. 类型推断只对赋值操作有效

    Eg:

    public class New{    public static 
    Map
    map(){ return new HashMap
    (); }}/** -- 方法中传参 这时编译器不会执行类型判断。在这种情况下,编译器认为:调用泛型方法后, 其返回值被赋给一个Object类型的变量*/public class Test{ public static void main(String args[]){ fun(New.map()); }}public class Test{ public static void main(String args[]){ fun(New.

参考资料:

[1]《Java编程的逻辑》
[2]《Thinking in Java》
[3]
[4]

转载地址:http://lmnsa.baihongyu.com/

你可能感兴趣的文章
大数据下的Distinct Count(一):序
查看>>
android 打包
查看>>
FUCKED-BUG之临时对象的生死
查看>>
一句话开启XP_CMDSHELL
查看>>
【100题】第四十五题 雅虎面试两道题(矩阵判断、数组划分)
查看>>
MySQL基础知识
查看>>
HTML页面优化
查看>>
centos6下安装docker
查看>>
常见的算法PHP 版,自整理
查看>>
使用UITableView隐藏的复选功能
查看>>
自定义下拉菜单(按钮下面出现下拉菜单),失去焦点后,如何下拉菜单自动消失,以及弹出窗体位置一直变化问题...
查看>>
uboot指令和环境变量
查看>>
Python之模块(二)
查看>>
Python跳出循环语句continue与break的区别
查看>>
内存中堆,栈的区别
查看>>
JavaScript
查看>>
django 配置邮件发送 send_email
查看>>
程序员聊人生
查看>>
ScrollView中嵌套WebView SrcollView自动向下滚动
查看>>
Python尾递归-创始人为何不愿TRE以及我们如何模拟TRE
查看>>