泛型

作者: stoneyang94 | 来源:发表于2018-05-16 17:00 被阅读0次

集合中的元素限定为一个特定的类型

一. 为什么需要泛型?

泛型出现之前

远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的。当时 Java 程序员们写集合类的代码都是类似于下面这样:

List list = new ArrayList();
list.add("www.cnblogs.com");
list.add(23);
String name = (String)list.get(0);
Integer number = (Integer)list.get(1);

在代码中声明一个集合,我们可以往集合中放入各种各样的数据,而在取出来的时候就进行强制类型转换。但其实这样的代码存在一定隐患,因为可能过了不久我们就会忘记到底我们存放的 list 里面到底第几个是 String,第几个是 Integer 了。这样就会出现下面这样的情况:

List list = new ArrayList();
list.add("www.cnblogs.com");
list.add(23);
String name = (String)list.get(0);
String number = (String)list.get(1);    //ClassCastException
  • 编译时正常
    开始时定义了一个List类型的集合,先向其中加入了一个字符串类型的值和一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型。
  • 运行时异常
    代码编译阶段正常,而运行时会现 “java.lang.ClassCastException” 异常。并且,导致此类错误编码过程中不易发现。

分析问题

  • 乱放
    当我们将一个对象放入集合中,集合不会记住此对象的类型,当再次从集合中取出此对象时,改对象的编译类型变成了Object类型,但其运行时类型任然为其本身类型。
  • 随缘取出
    因此,取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常。

解决办法

那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是使用泛型。

什么是泛型

例子

public class GenericDemo {
    public static void main(String[] args) {
        List<String> list = new ArrayList();
        list.add("datiangou");
        list.add("cimutongzi");
        for (int i = 0; i < list.size(); i++) {
            String name = list.get(i); 
            System.out.println("name "+i+" is " +name);
        }
    }
}

泛型
  • 泛型就是将类型参数化,其在编译时才确定具体的参数。在上面这个例子中,这个具体的类型就是 String。可以看到我们在创建 List 集合的时候指定了 String 类型,这就意味着我们只能往 List 集合中存放 String 类型的数据。
  • 而当我们指定泛型之后,我们去取出数据后就不再需要进行强制类型转换了,这样就减少了发生强制类型转换的风险。

术语

术语 解释
ArrayList<E> 泛型类型
ArrayList 原始类型
E 类型参数
<> 读作”typeof”
ArrayList<Integer> 参数化的类型
Integer 实际类型参数

泛型的特性(擦除)

public class GenericDemo {
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        ArrayList<Integer> b = new ArrayList<Integer>();
        Class c1 = a.getClass();
        Class c2 = b.getClass();
        System.out.println(c1 == c2);//true
    }
}
  • 输出的结果是 true。
    因为无论对于 ArrayList 还是 ArrayList,它们的 Class 类型都是一样的,都是 ArrayList.class
  • 那它们声明时指定的 String 和 Integer 到底体现在哪里呢?
    答案是体现在类编译的时候。当 JVM 进行类编译时,会进行泛型检查,如果一个集合被声明为 String 类型,那么它往该集合存取数据的时候就会对数据进行判断,从而避免存入或取出错误的数据。
  • 也就是说:泛型只存在于编译阶段,而不存在于运行阶段。在编译后的 class 文件中,是没有泛型这个概念的。

泛型通配符

”?”通配符

?表示任意类型,使用”?”通配符可以引用各种参数化的类型,可以调用与参数化无关的方法(如size()方法),不能调用与参数化有关的方法(如add()方法)

扩展

extends 限定通配符的上边界

Integer继承自Number,编译通过:

ArrayList<? extends Number > collection1= new ArrayList<Integer >();//编译通过

String不继承自Number,编译不通过:

ArrayList<? extends Number > collection2= new ArrayList<String>();//编译不通过

super限定通配符的下边界

NumberInteger父类,编译通过:

ArrayList<? super Integer > collection3= new ArrayList<Number >();//编译通过

NumberString父类,编译通过:

ArrayList<? super Integer > collection4= new ArrayList<String>();//编译不通过

缺陷

  • 对于 extends 通配符,我们无法向其中加入任何对象,但是我们可以进行正常的取出。extends 通配符偏向于内容的获取。
  • 对于 super 通配符,我们可以存入 T 类型对象或 T 类型的子类对象,但是我们取出的时候只能用 Object 类变量指向取出的对象。super 通配符更偏向于内容的存入。
  • 我们有一个PECS 原则(Producer Extends Consumer Super)很好的解释了这两个通配符的使用场景。
    • Producer Extends 说的是当你的情景是生产者类型,需要获取资源以供生产时,我们建议使用 extends 通配符,因为使用了 extends 通配符的类型更适合获取资源。
    • Consumer Super 说的是当你的场景是消费者类型,需要存入资源以供消费时,我们建议使用 super 通配符,因为使用 super 通配符的类型更适合存入资源。
  • 如果既想存入,又想取出,不要使用 extends 或 super 通配符。

参考文章
大白话说Java泛型:入门、使用、原理
java 泛型详解-绝对是对泛型方法讲解最详细的,没有之一
java基础巩固笔记(2)-泛型

相关文章

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • 【泛型】通配符与嵌套

    上一篇 【泛型】泛型的作用与定义 1 泛型分类 泛型可以分成泛型类、泛型方法和泛型接口 1.1 泛型类 一个泛型类...

  • 泛型的使用

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法 泛型类 泛型接口 泛型通配符 泛型方法 静态方法与...

  • Java 泛型

    泛型类 例如 泛型接口 例如 泛型通配符 泛型方法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型上下边...

  • 探秘 Java 中的泛型(Generic)

    本文包括:JDK5之前集合对象使用问题泛型的出现泛型应用泛型典型应用自定义泛型——泛型方法自定义泛型——泛型类泛型...

  • Web笔记-基础加强

    泛型高级应用 自定义泛型方法 自定义泛型类 泛型通配符? 泛型的上下限 泛型的定义者和泛型的使用者 泛型的定义者:...

  • 重走安卓进阶路——泛型

    ps.原来的标题 为什么我们需要泛型? 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析); 如何...

  • Kotlin泛型的高级特性(六)

    泛型的高级特性1、泛型实化2、泛型协变3、泛型逆变 泛型实化 在Java中(JDK1.5之后),泛型功能是通过泛型...

  • Java 19-5.1泛型

    泛型类定义泛型类可以规定传入对象 泛型类 和泛型方法 泛型接口 如果实现类也无法确定泛型 可以在继承类中确定泛型:

  • 【Swift】泛型常见使用

    1、Swift泛型4种 泛型函数泛型类型泛型协议泛型约束 2、泛型约束3种 继承约束:泛型类型 必须 是某个类的子...

网友评论

      本文标题:泛型

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