(三)泛型-协变和逆变的概念-泛型是不可变的

学习泛型的时候遇到了协变和逆变的概念,其实从某种程度上来说,这是可以略过的,这种概念的东西乍一看很容易让人不知所云。之所以说可以略过,是因为协变和逆变是一个单独存在的概念,然后恰巧在泛型的某些情况中有所体现。
更多的时候,大家更喜欢使用数组举例,然后说数组是协变的。本文将先讲述协变和逆变的概念,然后再举出数组的协变的例子,然后是泛型的协变和逆变的例子。

协变和逆变是函数中的概念

首先设计到一个函数的概念,协变和逆变是表示 函数变量关系与函数关系的特性表述。
比如 1<2 , 然后 函数f(x)=x+1,那么 f(1)< f(2),再进一步拓展假如 1<=2,并且f(1)<=f(2),那么我们就说 f(x)协变 的。
再比如,1<2,f(x)=-x,这个时候就有 f(1) >f(2) ,再进一步扩展,1<=2,并且f(1)>=f(2),那么我们就说f(x)是逆变的。

数组就像一种函数,变量是某种Java类或者接口,数组是协变的

在 Java 中,向上转化和向下转化都是基于具体类或者接口来说的,比如Integer、Number、Object,那么Integer[]是不是一个单独的类型呢?是的,数组是新组合起来的类型,即由某种类或接口产生了一种变化,等同于数学中称之为函数的东西。

比如这里,IntegerNumber 的子类,那么经过一个变化之后,比如 Integer[]Number[]之后,二者会是什么关系呢?如下代码:

    @Test  
    public void test(){  
        Number[] array1=new Number[1];//合法  
        Number[] array2=new Integer[1];//合法  
        //Integer[] array3=new Number[1];//编译报错  
    }  

Java 多态中有一个子类概念,显然,new Number[1] 在这里类似于 Number[] 的子类,然后第三行是编译报错,如果把数组看做一个函数,那么满足 x<=y , f(x)<=f(y),所以数组是协变的,第三行表示数组不是逆变的。

泛型是不可变的

上文已经说了,协变与逆变的概念,以及数组是协变的。

泛型是不可变的,即 ArrayList<Number>ArrayList<Integer>并不具有 NumberInteger那样的关系,这是因为泛型是在编译期检测类型合法性的,而在实际运行期类型被擦除了,所以在泛型看来 ArrayList<Number>ArrayList<Integer> 是两种不同的类型。
如下代码会编译报错:

    @Test  
    public void test1(){  
        ArrayList<Number> list=new ArrayList<Integer>();//编译报错  
    }  
泛型实现协变和逆变的特性-其他形式的泛型-彩蛋

不好意思哦,其他形式的泛型“犹抱琵琶半遮面”,下一篇见哦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java泛型中的协变逆变都是针对类型转换的规定。 协变(covariant):指的是继承链中子类(派生类)类型能够作为父类(基类)类型的一种属性,也就是子类可以作为父类使用的能力。在泛型中,协变概念可以用来表示如果类型A是类型B的一个子类型,那么泛型类G<A>就可以视作泛型类G<B>的一个子类型。 例子: ```java // Animal类 public class Animal {} // Dog类是Animal类的子类 public class Dog extends Animal {} // 泛型接口List public interface List<E> { void add(E e); E get(int index); } // 定义一个方法acceptList,其形参类型为List<? extends Animal> public static void acceptList(List<? extends Animal> list) { for (Animal animal : list) { // ... } } // List类型为List<Dog> List<Dog> list = new ArrayList<Dog>(); list.add(new Dog()); acceptList(list); // 在这里,我们可以传入一个List<Dog>参数,因为Dog类是Animal类的子类 ``` 逆变(contravariant):指的是继承链中父类(基类)类型能够作为子类(派生类)类型的一种属性,也就是父类可以作为子类使用的能力。在泛型中,逆变概念可以用来表示如果类型A是类型B的一个超类型,那么泛型类G<B>就可以视作泛型类G<A>的一个子类型。 例子: ```java // Animal类 public class Animal {} // Dog类是Animal类的子类 public class Dog extends Animal {} // 泛型接口Comparator public interface Comparator<T> { int compare(T o1, T o2); } // 定义一个方法sortList,其形参类型为List<? super Dog> public static void sortList(List<? super Dog> list) { // ... } // List类型为List<Animal> List<Animal> list = new ArrayList<Animal>(); list.add(new Animal()); sortList(list); // 在这里,我们可以传入一个List<Animal>参数,因为Animal类是Dog类的超类型 ``` extends和super关键字常常用于定义泛型类型参数的上边界(upper bound)和下边界(lower bound)。extends表示类型参数的上限,超过这个范围就会导致编译错误;super表示类型参数的下限,超过这个范围也会导致编译错误。 例子: ```java // 泛型类Pair,其类型参数T有上限(用extends)为Comparable<? super T>,表示类型T要么是Comparable<? super T>本身,要么是Comparable<? super T>的子类型 public class Pair<T extends Comparable<? super T>> { private T first; private T second; public Pair(T first, T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public T max() { return first.compareTo(second) >= 0 ? first : second; } } // Pair类型为Pair<String> Pair<String> pair = new Pair<String>("hello", "world"); String max = pair.max(); // 在这里,我们可以调用max方法,因为String类实现了Comparable<String>接口 ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值