首页 > 编程语言 >java 中协变,逆变,不变简单理解

java 中协变,逆变,不变简单理解

时间:2023-07-05 14:36:02浏览次数:28  
标签:中协变 java 逆变 animals 协变 泛型 new

1. 什么是协变、逆变、不变

假设有两个类,Dog和Animal,如果用Dog <= Animal 表示它俩的继承关系。用f(type) 表示类型构造器,一个已知的类型被类型构造器处理后就是一个崭新的类型。

协变就是f(Dog)是f(Animal)的子类,即f(Dog) <= f(Animal);逆变就是f(Animal)是f(Dog)的子类,即f(Aniaml) <= f(Dog);不变就是指f(Dog)与f(Animal)之间没有关系

类型构造器可以是泛型List<Animal>, 可以是数组 Animal[],可以是函数方法 method(Animal)

2.java 泛型和数组

  • java泛型不支持逆变和协变,只能是不变
 List<Animal> animals = new ArrayList<Dog>(); // 编译错误,java泛型不支持逆变和协变,只能是不变
  • java数组支持协变,不支持逆变,但也正因为支持协变,数组可能踩坑
 Animal[] animals = new Dog[10];
 animals[0] = new Dog();
 animals[1] = new Cat(); // 运行时异常 java.lang.ArrayStoreException

3.java 泛型协变&上界

  • 考虑泛型支持协变后有什么好处,解决了什么问题?
	// processAnimals方法中如果不支持泛型协变,那么难道要通过接收类型的不同重写好多的方法吗?太麻烦了!!!
    public static void main(String[] args) {
	processAnimals(new ArrayList<Cat>()); // 编译错误
	proceessAimals(new ArrayList<Dog>()); // 编译错误
	}
	 public static void processAnimals(List<Animal> animals) {
	// ...
    }
  • 在java泛型中加入extends关键字实现支持协变,<? extends Animal> 其中?代表不确定类型通配符,和extends结合就声明了泛型的上限,表示接收的类型只能是指定类型或是该类型的子类
  • 上面说数组支持协变,添加其他类型会出现运行时异常,泛型协变为了杜绝这种隐患,所以泛型协变除了null 可以写,其他的都不能写(编译异常),可以读
  • 指定上界的好处,限定类型(编译错误提醒);可以访问上界类型中的方法(要不只能访问Object类中方法)
	// 使用extends关键字让泛型支持协变,这样processAniamls方法中的泛型变量就能接收子类集合了
	public static void processAnimals(List<> extends Animal> anumals){
	
	}
	
	public static void processAnimalsExtends(List<? extends Animal> animals) {
        animals.add(null); // 正常
        /**
         * 协变不允许传入除null
         */
        // animals.add(new Animal()); // 编译错误
        // animals.add(new Dog()); // 编译错误
        animals.remove(new Dog()); // 不会破坏类型安全
        animals.contains(new Dog()); // 不会破坏类型安全
    }

	public class AKA<T extends Animal>{

    	public static void main(String[] args) {
        AKA<Animal> animalAKA = new AKA<>();
        AKA<Cat> catAKA = new AKA<>();
        AKA<String> stringAKA = new AKA<String>();// 编译错误
    }
}

4.java 泛型逆变&下界

  • 使用super关键字声明泛型下界,如<? super Dog>,逆变后就可以接收本类型或父类型的泛型类
  • 逆变可以添加元素,逆变泛型可以接受本类型及父类型元素,但是添加元素只能添加指定类型或指定类型的子类
	public static void testDog(List<? super Dog> dest,list<? extends Dog> src){
		for(Dog dog :src){
		if(dog.isHappy()){
		 dest.add(dog);
		}
		}
	}
	
	public static void main(Stringp[] args){
	testDog(new ArrayList<Animal>(),new ArrayList<哈士奇>()); // 正确
	}

4.应用场景

只读不写:用协变
只写不读:用逆变
又读又写:用不变

标签:中协变,java,逆变,animals,协变,泛型,new
From: https://www.cnblogs.com/hkz329/p/17528086.html

相关文章

  • JAVA设计模式之建造者模式
    设计模式设计模式(DesignPattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。总体来说设计模式分为三大类:创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、......
  • 单线程JavaScript为何如此高效
    原文合集地址如下,有需要的朋友可以关注本文地址合集地址什么是js执行机制JavaScript的执行机制指的是JavaScript代码在运行时的工作方式和顺序。它涉及以下几个关键概念:单线程:JavaScript是一门单线程的编程语言,意味着它只有一个主线程用于执行代码。这意味着JavaScrip......
  • java双冒号写法(Lambda的简写)
    类似这种Person::getName,双冒号写法,是Java8对Lambda表达式的简写常见的简写场景有以下是Java8中方法引用的一些语法:静态方法引用(staticmethod)语法:classname::methodname例如:Person::getAge对象的实例方法引用语法:instance::methodname例如:System.out::println对象的超类方......
  • 面向对象(java)
    一、定义 二、注意事项:一个java文件中最好只写一个类;  三、封装(1)对象代表什么,就得封装对应的数据,并提供数据对应的行为;(2)如果加了static,就是类的变量(类共享),而不是实例变量了;......
  • java运行ubuntu clear命令
    importjava.io.BufferedReader;importjava.io.InputStreamReader;publicclassClearScreen{publicstaticvoidmain(String[]args){try{//执行clear命令Processprocess=Runtime.getRuntime().exec("clear");......
  • JavaScript发展历史(JS)
    JavaScript发展历史1994年,网景公司(Netscape)发布了Navigator浏览器0.9版,这是世界上第一款比较成熟的网络浏览器,轰动一时。但是这是一款名副其实的浏览器--只能浏览页面,浏览器无法与用户互动,当时解决这个问题有两个办法,一个是采用现有的语言,许他们直接嵌入网页。另一个是发明一......
  • 关于Java已死,看看国外开发者怎么说的
    博主在浏览medium社区时,发现了一篇点赞量1.5k的文章,名称叫《JavaisDead—5MisconceptionsofdevelopersthatstillthinkJavaisrelevanttoday!》直译过来就是《Java已死—开发人员对Java在现代编程语言中的5个误解》。这篇文章可以说是标题党得典范,热度全靠标......
  • javax.net.ssl.SSLHandshakeException: The server selected protocol version TLS10
    问题:报错:javax.net.ssl.SSLHandshakeException:TheserverselectedprotocolversionTLS10isnotacceptedbyclientpreferences[TLS12]解决方式:1、修改%JAVA_HOME%/jre/lib/security/java.security2、修改内容:jdk.tls.disabledAlgorithms删除TLSv13、删除前: https:......
  • Java异常分类和常见异常
    1.异常异常:执行中发生的不正常情况。分为两类:Error:JVM系统内部错误、资源耗尽等严重情况。比如StackOverflowError和OutOfMemory。Exception:空指针访问、试图读取不存在的文件、网络连接中断、数组下标越界。解决办法:①终止程序运行;②错误检测提示处理。2.异常分类......
  • Java高级
    第06章:随堂复习与企业真题(面向对象-基础)一、随堂复习1.(了解)面向过程vs面向对象不管是面向过程、面向对象,都是程序设计的思路。面向过程:以函数为基本单位,适合解决简单问题。比如:开车面向对象:以类为基本单位,适合解决复杂问题。比如:造车2.类、对象类:抽象的,概念......