美文网首页
Java范型学习笔记

Java范型学习笔记

作者: zhizhuwang | 来源:发表于2017-11-29 17:30 被阅读29次

泛型的目的

  • 通过引入类型参数,使得相同的代码可以被复用;传入不同的类型参数,就可以适用于不同的场景。
  • 通过编译器来避免代码中可能存在的错误,在编译阶段排除可能存在的错误。

两种泛型

  • 泛型类
  • 泛型方法

满足is-a关系的变量都可以作为方法的参数。
但是,A和B之间的关系,以及A的泛型类与B的泛型类之间的关系,是两种不同的关系。
泛型类之间的可以存在继承关系。

类型推断

  • 类型推断:泛型方法在调用时,无需指定类型参数,编译器会从调用的实参中推断出这个泛型参数。

  • 泛型类和非泛型类中的泛型构造器的类型推导

受限制类型参数

  • 上限:T extends UpperBound
    限定一个未知的类型为一个指定的类型,或者此类型的子类型。

  • 上限通配符
    ? extends UpperBound
    具体的类型未知,但此类型为一个指定的类型,或者此类型的子类型。

  • 下限通配符
    ? super LowerBound

  • 未限定通配符

    • 通过 ? 符号指定。
    • 可以用于参数的类型、域的类型、本地变量的类型,有时也可用做返回值的类型;但是不作为泛型方法调用的参数、泛型类的实例化,以及超类型。
    • 使用通配符进行子类化,例如:List<?>List<String>List<Integer>的超类型。

通配符捕获(Wildcard Captrue)

在编译阶段对表达式求值时,编译器可以从代码中推测出一种特定的类型,这种情况被称为通配符捕获。

一般我们不用关注这个过程,除非编译过程中出现通配符捕获相关的错误。

void foo(List<?> i) {
        fooHelper(i);
    }


    // Helper method created so that the wildcard can be captured
    // through type inference.
    private <T> void fooHelper(List<T> l) {
        l.set(0, l.get(2));
    }

public class WildcardErrorBad {

    void swapFirst(List<? extends Number> l1, List<? extends Number> l2) {
      Number temp = l1.get(0);
      l1.set(0, l2.get(0)); // expected a CAP#1 extends Number,
                            // got a CAP#2 extends Number;
                            // same bound, but different types
      l2.set(0, temp);      // expected a CAP#1 extends Number,
                            // got a Number
    }
}

通配符的使用规则

对于通配符的使用来说,一个很让人迷惑的地方在于何时使用extends,何时使用super。总体上看,可以通过把参数分为以下两类来决定使用哪种关键字。

  • 变量
    提供数据,供本代码使用。
  • 变量
    保存数据,供其它地方的代码使用。

例如,对于Collectionscopy方法:
public static <T> void copy(List<? super T> dest, List<? extends T> src)
dest是出变量,src是入变量。

基于这种分类,可以约定以下使用规则:

  • 入变量,使用extends关键字定义变量的上限。
  • 出变量,使用super关键字定义变量的下限。
  • 入变量,准备调用Object类的方法,使用未受限通配符。
  • 同时作为入变量和出变量,不使用通配符。

简单地说,就是入变量只能读,不能写;出变量只能写,不能读。

特别注意
通过<? extends ...>定义的变量,可以被认为是一种只读的变量。例如: List<? extends ...>的变量只能执行以下几种操作:

  • add(null)
  • 调用clear
  • 获取iterator,调用remove
  • 捕获通配符,往其中添加从中读出来的元素。

扩展思考:

  • 为什么上限通配符定义变量只能读,不能写?
    对于使用? extends T定义的变量参数,可以是T的任意子类。
    在执行读操作的时候,可以用T来引用读出来的元素,因为T是这些元素类型的父类。
    但是,如果执行写操作,编译器不知道写入的是T的哪个子类型。假设A、B均是T的子类,并且B继承A,即T <- A <-- B。编译器无法推断出写入的元素类型是T,还是A,还是B。

  • 为什么下限通配符定义的变量只能写,不能读?
    对于使用? super T定义的变量参数,可以是T的任意父类。
    在执行写操作的时候,无论实际是哪种类型,都是T的父类。
    执行读操作的时候,编译器无法推断出读出的元素类型,不知道该用什么类型去引用。

相关文章

  • Java范型学习笔记

    泛型的目的 通过引入类型参数,使得相同的代码可以被复用;传入不同的类型参数,就可以适用于不同的场景。 通过编译器来...

  • CoreJava笔记 - 范型程序设计(2)

    范型代码和Java虚拟机 关键知识:类型擦除Java的范型是编译器层次的范型,而在Java虚拟机上并没有范型类。在...

  • CoreJava笔记 - 范型程序设计(5)

    反射与范型 由于类型擦除,反射无法得到关于范型类型参数的信息。 范型的Class类在Java的反射库中,Class...

  • Java 范型

    范型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。范型的本质是参数化类型,也就是说所操作...

  • java范型

    原始文件: https://pan.baidu.com/s/1cLBYSe , 提取码: eccp

  • java范型

    Java 语言中,引入泛型实乃为一个较大的功能增强。不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库...

  • Java 范型

    假定有一个这样的需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?答案是...

  • Generic 范型 - type parameter

    范型简介 从JDK 5.0开始,范型作为一种新的扩展被引入到了java语言中。 有了范型,我们可以对类型(type...

  • Effective Java 3rd-----Chapter 5

    Chapter 5 Generics 范型 SINCE Java 5, generics have been a ...

  • 【JAVA】浅谈Java范型

    1.Java泛型是什么?2.通常的泛型的写法示例3.类型擦除4.为什么要使用Java泛型5.通过示例了解PECS原...

网友评论

      本文标题:Java范型学习笔记

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