Java泛型的协变

  在上篇《Java泛型的基本使用》这篇文章中遗留下面问题,即将子类型也能加入到父类型的泛型中。要实现这样的功能必须借助于协变。

实验准备

  如今在上篇文章展示的Decorator类型的基础上,添加一些代码,如代码清单1所看到的。

代码清单1

   /**
     *
     * 描 述:Exp2使用br/>
     * 作 者:jiaan.gja<br/>
     * 历 史: (版本号) 作者 时间 凝视 <br/>
     * @param itemList
     */
    public void doDecorate2(List<? extends T> itemList) {
        for(int i = 0; i < itemList.size(); i++) {
            System.out.println(itemList.get(i));
        }
    }

    /**
     *
     * 描 述:Exp2使用<br/>
     * 作 者:jiaan.gja<br/>
     * 历 史: (版本号) 作者 时间 凝视 <br/>
     * @param itemList
     * @param t
     */
    public void addDecorate2(List<? extends T> itemList, T t) {
    // itemList.add(t);
    }

能够看到这跟《Java泛型的基本使用》中的doDecorate和addDecorate方法差点儿一样,唯一的差别是将List<T>改为List<?
extends T>,这样的语法被称为协变。假设你已经在IDE里写了上面的代码。肯定发现addDecorate2中的语句itemList.add(t);已经编译报错。所以我们先将itemList.add(t);这条语句临时凝视掉。先来看看doDecorate2有没有达到我们的预期。

实验:泛型协变

  如今我们尝试下协变的用途。即调用doDecorate2方法,如代码清单2所看到的。

代码清单2

/**
 *
 * 类 名: Exp2<br/>
 * 描 述: 泛型的协变使用<br/>
 * 作 者: jiaan.gja<br/>
 * 创 建: 2015年8月20日<br/>
 *
 * 历 史: (版本号) 作者 时间 凝视
 */
class Exp2 {
    public static void main(String[] args) {

        Decorator<Auction> decoratorA = new Decorator<Auction>();
        List<Auction> listA = new ArrayList<Auction>();
        Auction auctionOne = new Auction("auctionOne");
        Auction auctionTwo = new Auction("auctionTwo");
        decoratorA.addDecorate2(listA, auctionOne);
        decoratorA.addDecorate2(listA, auctionTwo);
        decoratorA.doDecorate2(listA);

        Decorator<Table> decoratorB = new Decorator<Table>();
        List<Table> listB = new ArrayList<Table>();
        Table tableOne = new Table("tableOne", 10);
        Table tableTwo = new Table("tableTwo", 20);
        decoratorB.addDecorate2(listB, tableOne);
        decoratorB.addDecorate2(listB, tableTwo);
        decoratorB.doDecorate2(listB);

        Decorator<Service> decoratorC = new Decorator<Service>();
        List<Service> listC = new ArrayList<Service>();
        Service serviceOne = new Service("serviceOne", "methodOne");
        Service serviceTwo = new Service("serviceTwo", "methodTwo");
        decoratorC.addDecorate2(listC, serviceOne);
        decoratorC.addDecorate2(listC, serviceTwo);
        decoratorC.doDecorate2(listC);

        /*
         * 协变測试開始
         */
        decoratorA.doDecorate2(listB);
        decoratorA.doDecorate2(listC);

    }
}

首先。我们看到doDecorate2方法至少能达到《Java泛型的基本使用》中的doDecorate方法的效果。

因为声明了decoratorA的类型是Decorator<Auction>,那么此时实例decoratorA的doDecorate2的參数能够觉得是以下这样:

List<? extends Auction> itemList

而listB的类型是ArrayList<Table>,listC的类型是ArrayList<Service>,也就是说协变同意:

List<? extends Auction> itemList = new ArrayList<Auction>();
List<? extends Auction> itemList = new ArrayList<Table>();
List<? extends Auction> itemList = new ArrayList<Service>();

这说明itemList中既能够存储Auction。也能够存储Table和Service。

我们做个如果,当你想从itemList中获取Table的时候,Java编译器怀疑这实际可能是个Service。因此会有编译错误。如果想从itemList中获取Auction是没有问题的,由于不管是Table还是Service它们都能够被自己主动转换为父类Auction。这些猜測,能够通过代码清单3验证。

代码清单3

        List<?

extends Auction> itemList = listA;
        Auction a = itemList.get(0);
        Table t = itemList.get(0);
        Service s = itemList.get(0);
        itemList = listB;
        a = itemList.get(0);
        t = itemList.get(0);
        s = itemList.get(0);
        itemList = listC;
        a = itemList.get(0);
        t = itemList.get(0);
        s = itemList.get(0);

相同的道理,当你想要向itemList中加入Table时,编译器怀疑itemList实际是个ArrayList<Service>,因此是不同意的。在获取Auction时是没有问题的,那么加入Auction会不会也能够?因为itemList可能是ArrayList<Service>或者ArrayList<Table>,依据上一篇《Java泛型的基本使用》的结论,这实际也是不能够的。能够用下列代码验证:

        Auction auctionThree = new Auction("auctionThree");
        itemList.add(auctionThree);
        Auction tableThree = new Auction("tableThree");
        itemList.add(tableThree);
        Auction serviceThree = new Auction("serviceThree");
        itemList.add(serviceThree);

因此这也说明了刚開始看到的addDecorate2中的语句itemList.add(t);为什么会编译报错。尽管如此,我们依旧希望能将子类型加入到父类型声明的泛型中。这该怎么办?Java当然也考虑到这个问题。请看下一篇《Java泛型的逆变》

总结

  假设从泛型中获取子类,应该使用协变。

时间: 2024-10-20 00:28:06

Java泛型的协变的相关文章

JAVA泛型的基本使用

Java1.5版本号推出了泛型,尽管这层语法糖给开发者带来了代码复用性方面的提升,可是这只是是编译器所做的一层语法糖,在真正生成的字节码中,这类信息却被擦除了. 笔者发现非常多几年开发经验的程序猿,依旧不善于使用Java泛型,本文将从Java泛型的基本使用入手,在今后的多篇博文里.对泛型的使用做个总结.本文不会深入Java泛型的实现原理.仅仅会介绍Java泛型的使用. 实验准备 首先须要创建一个类继承体系.以商品为例,每种商品都有主要的名称属性.在大数据应用中,数据表和服务都能够作为商品,表有行

Java泛型的逆变

在上篇<Java泛型的协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码清单1 /** * * 描 述:Exp3使用逆变<br/> * 作 者:jiaan.gja<br/> * 历 史: (版本) 作者 时间 注释 <br/> * @param itemList */ public void doDecorate3(List<? super

JAVA泛型——基本使用

Java1.5版本推出了泛型,虽然这层语法糖给开发人员带来了代码复用性方面的提升,但是这不过是编译器所做的一层语法糖,在真正生成的字节码中,这类信息却被擦除了.笔者发现很多几年开发经验的程序员,依然不善于使用Java泛型,本文将从Java泛型的基本使用入手,在今后的多篇博文里,对泛型的使用做个总结.本文不会深入Java泛型的实现原理,只会介绍Java泛型的使用. 实验准备 首先需要创建一个类继承体系.以商品为例,每种商品都有基本的名称属性.在大数据应用中,数据表和服务都可以作为商品,表有行数属性

Java中泛型的协变

在工作中遇到一个问题,用代码描述如下: package test; import java.util.LinkedList; import java.util.List; public class ListTest {     public void func(List<Base> list) {     }     public static void main(String args[]) {         ListTest lt = new ListTest();         Li

Java泛型中的协变和逆变

Java泛型中的协变和逆变 一般我们看Java泛型好像是不支持协变或逆变的,比如前面提到的List<Object>和List<String>之间是不可变的.但当我们在Java泛型中引入通配符这个概念的时候,Java 其实是支持协变和逆变的. 看下面几行代码: // 不可变 List<Fruit>fruits =newArrayList<Apple>();// 编译不通过 // 协变 List<?extendsFruit>wildcardFruit

浅谈Java泛型中的extends和super关键字(转)

泛型是在Java 1.5中被加入了,这里不讨论泛型的细节问题,这个在Thinking in Java第四版中讲的非常清楚,这里要讲的是super和extends关键字,以及在使用这两个关键字的时候为什么会不同的限制.    首先,我们定义两个类,A和B,并且假设B继承自A.下面的代码中,定义了几个静态泛型方法,这几个例子随便写的,并不是特别完善,我们主要考量编译失败的问题: public class Generic{ //方法一 public static <T extends A> void

1月21日 - (转)Java 泛型

java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 M

java泛型的讲解

java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 M

我所认识的java泛型(主要讨论通配符的使用)

在使用java泛型的时候,经常会遇到类似的声明<?extends XXX>或者<? super XXX>,XXX代表一个类或接口.这到底是什么意思呢?今天看了些书有了一点儿体会,不知恰当与否,忘讨论之. 首先需要清楚一个事实,在java中上转型是安全的,下转型是不安全的.例如:String类是Object类的子类,我们可以将String强转为Object但是却不能将Object类强转为一个String,这很明显,因为在String类中包含了Object类没有的方法: 再来看一个事实