java教程——泛型

Posted 我想月薪过万

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java教程——泛型相关的知识,希望对你有一定的参考价值。

在 java教程——泛型(三)中我们讲到了 泛型的实现原理 和 继承,这一节我们来详细讲一下 extends通配符 的使用。

谈到 extends 这个关键字,大家的第一反应肯定是类的继承。对,有这么个感觉的同学说明你的灵感来了,就在这个时候我们来看一下这么个现象。

“灵异现象”

  • 带泛型的类作为参数传递时的问题
package test;
public class changeData {

    public static void main(String[] args) {
        selfMap<Number> sf = new selfMap<>(1, 2);
        System.out.println(add(sf));
    }

    static int add(selfMap<Number> selfMap){
        Number oneValue = selfMap.getOne();
        Number twoValue = selfMap.getTwo();
        return oneValue.intValue() + twoValue.intValue();
    }
}
class selfMap<T>{
    private T one;
    private T two;

    public selfMap(T one, T two) {
        this.one = one;
        this.two = two;
    }

    public T getOne(){
        return one;
    }

    public T getTwo(){
        return two;
    }
}

从运行结果可以看出,运行上面的代码是完全没问题的 。

问题一:将 add() 方法中的两个 Number 改为 Integer?

解:显然编译都通过不了。其实原因很简单,就是一个向下转型的问题,Number 转 Integer,你可知道 Number 的前身是什么,不知道的话你在这瞎转,编译器能打印吗?显然不可以。

问题二:我们往 add() 方法里改传 selfMap<Integer> 可以吗?

解:肯定是不可以的,我们在 java教程——泛型(一)中的 向上转型 的时候说过 selfMap<Integer> 不是 selfMap<Number> 的子类,所以,报错是可想而知的。

当 selfMap<Number> 作为函数参数时,该如何传一个 selfMap<Integer> 呢

这个时候 extends通配符 就起作用了,我们可以这么写,代码如下:

package test;

import java.lang.reflect.Field;
import java.util.Arrays;

public class changeData {

    public static void main(String[] args) {
        selfMap<Integer> sf = new selfMap<>(1, 2);
        System.out.println(add(sf));
    }

    static int add(selfMap<? extends Number> selfMap){
        Number oneValue = selfMap.getOne();
        Number twoValue = selfMap.getTwo();
        return oneValue.intValue() + twoValue.intValue();
    }
}
class selfMap<T>{
    private T one;
    private T two;

    public selfMap(T one, T two) {
        this.one = one;
        this.two = two;
    }

    public T getOne(){
        return one;
    }

    public T getTwo(){
        return two;
    }
}

你一眼望去可能真看不出有什么区别,可是你细细一看,就会发现我们把 add(selfMap<Number> selfMap) 改成了 add(selfMap<? extends Number> selfMap)

也就是说解决问题的关键在于这么一句申明 <? extends Number> 那它到底是什么呢?

这种使用 <? extends Number>  的泛型定义称之为上界通配符(Upper Bounds Wildcards),即把泛型类型  的上界限定在Number了。

除了可以传入 Pair<Integer> 类型,我们还可以传入 Pair<Double> 类型,Pair<BigDecimal> 类型等等,因为 Double 和 BigDecimal 都是 Number 的子类。

问题二我们算是解决了,可是问题一我们能解决吗?答案是不可以写成 Number 的子类的,因为你可以传进来的类型太多了,编译器只能确定类型一定是Number的子类(包括Number类型本身),但具体类型无法确定。

Set方法的调用

我们把 class selfMap<T> 完善一下,写两个 set 方法玩一下,如下:

class selfMap<T>{
    private T one;
    private T two;

    public selfMap(T one, T two) {
        this.one = one;
        this.two = two;
    }

    public T getOne(){
        return one;
    }

    public T getTwo(){
        return two;
    }

    public void setOne(T one) {
        this.one = one;
    }

    public void setTwo(T two) {
        this.two = two;
    }
}

这个时候我们就可以 调用 里面的 set 方法了,当我们 在 add(selfMap<? extends Number> selfMap) 方法里传入的是 selfMap<? extends Number> selfMap 类型的参数时,根据 “擦试法” 我们可以 明白 真正的 set 方法的写法:

setOne(? extends Number one)

也就是说他需要你传一个 ? extends Number 类型的参数,有人说这个简单啊,不就是 Number 的子类吗?,其实不然,简单来解释还是那个原因:Number子类很多啊,如何保证安全的类型转换呢?很难,所以啊,<? extends Number>通配符的一个重要限制:方法参数签名 setOne(? extends Number)无法传递任何Number类型及其子类型给setOne(? extends Number)。简单点说就是 :你把 ? extends Number当做泛型T传入会导致该类的set方法不方便调用。

因此,方法参数类型SelfMap<? extends Number>表明了该方法内部只会读取SelfMap的元素,不会修改SelfMap的元素(因为无法调用setOne(? extends Number)方法。换句话说,这是一个对参数SelfMap<? extends Number>进行只读的方法(恶意调用setOne(null)除外)。

使用extends限定T类型

在定义泛型类型Pair<T>的时候,也可以使用extends通配符来限定T的类型:

public class Pair<T extends Number> { ... }

现在,我们只能定义:

Pair<Number> p1 = null;
Pair<Integer> p2 = new Pair<>(1, 2);
Pair<Double> p3 = null;

因为NumberIntegerDouble都符合<T extends Number>

Number类型将无法通过编译:

Pair<String> p1 = null; // compile error!
Pair<Object> p2 = null; // compile error!

因为StringObject都不符合<T extends Number>,因为它们不是Number类型或Number的子类。

小结

使用类似<? extends Number>通配符作为方法参数时表示:

  • 方法内部可以调用获取Number引用的方法,例如:Number n = obj.getFirst();

  • 方法内部无法调用传入Number引用的方法(null除外),例如:obj.setFirst(Number n);

即一句话总结:使用extends通配符表示可以读,不能写。

使用类似<T extends Number>定义泛型类时表示:

  • 泛型类型限定为Number以及Number的子类。

以上是关于java教程——泛型的主要内容,如果未能解决你的问题,请参考以下文章

java教程——泛型

Java泛型:类型擦除

201621123062《java程序设计》第九周作业总结

Java入门 - 高级教程 - 03.泛型

Java入门 - 高级教程 - 03.泛型

什么意思 在HashMap之前 ? Java中的泛型[重复]