焦点短讯!From Java To Kotlin 2:Kotlin 类型系统与泛型
时间:2023-06-06 09:26:33 来源:博客园
上期主要分享了 From Java To Kotlin 1 :空安全、扩展、函数、Lambda。
这是 From Java to Kotlin 第二期。 From Java to Kotlin 关键在于 思维的转变。
表达式思维Kotlin 中大部分语句是表达式。 表达式思维是一种编程思维。 编程思维是一种非常抽象的概念,很多时候是只可意会不可言传的。 不过,从某种程度上看,学习编程思维,比学习编程语法更重要。因为编程思维决定着我们的代码整体的架构与风格,而具体的某个语法反而没那么大的影响力。当然,如果对 Kotlin 的语法没有一个全面的认识,编程思维也只会是空中楼阁。就像,我们学会了基础的汉字以后开始写作文:学了汉字以后,如果没掌握写作的技巧,是写不出好的文章的。同理,如果学了 Kotlin 语法,却没有掌握它的编程思维,也是写不出优雅的 Kotlin 代码的。
(资料图片仅供参考)
下面我们看一段 Kotlin 代码
//---1vari=0if(data!=null){i=data}//---2varj=0if(data!=null){j=data}else{j=getDefault()println(j)}//---3vark=0if(data!=null){k=data}else{throwNullPointerException()}//---4varx=0when(data){isInt->x=dataelse->x=0}//---5vary=0try{y="Kotlin".toInt()}catch(e:NumberFormatException){println(e)y=0}
这些代码,如果我们用平时写 Java 时的思维来分析的话,是挑不出太多毛病的。但是站在 Kotlin 的角度,就完全不一样了。利用 Kotlin 的语法,我们完全可以将代码写得更加简洁,就像下面这样:
//---1vali=data?:0//---2valj=data?:getDefault().also{println(it)}//---3valk=data?:throwNullPointerException()//---4valx=when(data){isInt->dataelse->0}//---5valy=try{"Kotlin".toInt()}catch(e:NumberFormatException){println(e)0}
这段代码看起来就简洁了不少,所以从 Java 转到 Kotlin 要格外注意思维转变,培养表达式思维。
这里有个疑问:Kotlin 为什么就能用这样的方式写代码呢?其实这是因为:if、when、throw、try-catch 这些语法,在 Kotlin 当中都是表达式。
那么,这个“表达式”到底是什么呢?其实,与表达式(Expression)对应的,还有另一个概念,我们叫做语句(Statement)。
表达式(Expression),是一段可以产生值的代码;
语句(Statement),则是一句不产生值的代码。
我们可以简单来概括一下:表达式(Expression)有值,而语句(Statement)不总有。
用一个更详细的例子解释:
vala=1//statementprintln(a)//statement//statementvari=0if(data!=null){i=data}//1+2是一个表达式,但是对b的赋值行为是statementvalb=1+2//ifelse整体是一个表达式//a>b是一个表达式,子表达式//a-b是一个表达式,子表达式//b-a是一个表达式,子表达式。funminus(a:Int,b:Int)=if(a>b)a-belseb-a//throwNotImplementedError()是一个表达式funcalculate():Int=throwNotImplementedError()
这段代码是描述了常见的 Kotlin 代码模式,从它的注释当中,我们其实可以总结出这样几个规律:
赋值语句,就是典型的 statement;
if 语法,既可以作为语句,也可以作为表达式;
语句与表达式,它们可能会出现在同一行代码中,比如 val b = 1 + 2;
表达式还可能包含“子表达式”,就比如这里的 minus 方法;
throw 语句,也可以作为表达式。
看到这里,可能又有一个疑问,那就是:calculate() 这个函数难道不会引起编译器报错吗?
//函数返回值类型是Int,实际上却抛出了异常,没有返回Int//↓↓funcalculate():Int=throwNotImplementedError()
要想搞清楚这个疑问, 需要理解Kotlin的类型系统。
小结Koltin表达式思维是指时刻记住 Kotlin 大部分的语句都是表达式,它们可以产生返回值。利用这种思维,往往可以大大简化代码逻辑。Kotlin 的类型系统类、类型和子类型类(class)是指一种数据类型,类定义定义对象的属性和方法,可以用来创建对象实例,例如 class Person(val name: String)
,用于表示一个人的属性和行为。
类型(type)是指一个_变量或表达式 **的 **数据类型_。类型可以用来描述变量或表达式的特征和限制(取值范围和可用的操作)。在Kotlin中,每个变量或表达式都有一个确定的类型,例如Int、String、Boolean等,类型可以是可空的或非空的,例如 String?
或 String
。
子类型(subtype)是指一个类型的子集,即一个类型的值可以赋值给另一个类型的变量或表达式。例如 class Student(name: String, val grade: Int) : Person(name)
中,Student
是 Person
的子类型,String
是 String?
的子类型 。
在 Kotlin 中,类和类型之间有一定的对应关系,但并不完全相同。一个类可以用于构造多个类型, 例如泛型类 List
可以构造出 List
、List
等不同的类型。一个类型也可以由多个类实现,例如接口类型 Runnable
可以由多个实现了 run()
方法的类实现。
先看一段代码:
非可空类型的 strNotNull:String ,可以赋值给 可空类型的strNullable:String? ; 可空类型的strNullable:String? 不可以赋值给 非可空类型的 strNotNull:String。
可以看出每一个Kotlin类都可以用于构造至少两种类型。
根据子类型化的定义,String 是 String?的子类型。
看到这里可能有个疑问?没有继承关系,String 并没有 继承 String?,为啥String是 String? 的子类型。
其实我也有, 经常开发 Java 会有一个误区:认为只有继承关系的类型之间才可以有父子类型关系。
因为在Java中,类与类型大部分情况下都是“等价”的(在Java泛型出现前)。事实上,“继承”和“子类型化”是两个完全不同的概念。子类型化的核心是一种类型的替代关系。
子类型化, 以下内容引用自维基百科
在编程语言理论中,子类型(动名词,英语:subtyping(也有翻译为子类型化))是一种类型多态的形式。这种形式下,子类型(名词,英语:subtype)可以替换另一种相关的数据类型(超类型,英语:supertype)。也就是说,针对超类型元素进行操作的子程序、函数等程序元素,也可以操作相应的子类型。如果 S 是 T 的子类型,这种子类型关系通常写作 S <: T,意思是在任何需要使用 T 类型对象的_环境中,都可以安全地使用_ S 类型的对象。
由于子类型关系的存在,某个对象可能同时属于多种类型,因此,子类型(英语:subtyping)是一种类型多态的形式,也被称作子类型多态(英语:subtype polymorphism)或者包含多态(英语:inclusion polymorphism)。
子类型与面向对象语言中(类或对象)的继承是两个概念。子类型反映了类型(即面向对象中的接口)之间的_关系_;而继承反映了一类对象可以从另一类对象创造出来,是_语言特性 _的实现。因此,子类型也称接口继承;继承称作实现继承。
子类型 - 维基百科,自由的百科全书
子类型化可表示为:
S<:T
以上S是T的子类,这意味着在需要T类型 值的地方,S类型的 值同样适用,可以用 S 类型的 值替换。
所以在前面的例子中, 虽然String与String?看起来没有继承关系,然而在我们需要用String?类型值的地方,显然可以传入一个类型为String的值,这在编译上不会产生问题。反之却不然。 所以String?是String的父类型。
继承强调的是一种“实现上的复用”,而子类型化是一种类型语义的关系,与实现没关系。对于 Java 语言,由于一般在声明父子类型关系的同时也声明了继承的关系,所以造成了某种程度上的混淆。
类型系统Kotlin 的类型还分为可空类型和不可空类型。Any 是所有非空类型的根类型;而 Any? 是所有可空类型的根类型。 我们猜测 Kotlin 的类型体系可能是这样的:那Any 与 Any? 之间是什么关系呢?
Any 、Any?与 Java 的 ObjectJava 当中的 Object 类型,对应 Kotlin 的“Any?”类型。但两者并不完全等价,因为 Kotlin 的 Any 可以没有 wait()、notify() 之类的方法。因此,我们只能说 Kotlin 的“Any?”与 Java 的 Object 是大致对应的。
下面是Java 代码,它有三个方法,分别是可为空的 Object 类型、不可为空的 Object 类型,以及无注解的 Object 类型。
publicclassTestTypeJava{@Nullable//可空注解publicObjecttest(){returnnull;}//默认publicObjecttest1(){returnnull;}@NotNull//不可空注解publicObjecttest2(){return1;}}
上面的代码通过 Convert Java File to Kotlin File
转换成 Kotlin:
classTestTypeJava{//可空注解funtest():Any?{returnnull}funtest1():Any?{//可以看出默认情况下,JavaObject对应KotlinAny?returnnull}//不可空注解funtest2():Any{return1}}
可以看出默认情况下,没有注解标记可空信息的时候, Java Object 对应 Kotlin Any?。
有些时候Java代码包含了可空性的信息,这些信息使用注解来表达。当代码中出现了这样的信息时,Kotlin就会使用它。因此Java中的@Nullable String被Kotlin当作String?,而@NotNull String就是String
如果没有是否可空注解, Java类型会变成 Kotlin 中的平台类型(后面会解释)。
了解了 Any 和 Any?的关系,可以画出关系图
Unit 与 Void 与 void先看一段 Java 代码
publicclassPrintHello{publicvoidprintHelloWorld(){System.out.println("HelloWorld!");}}
转成 Kotlin
classPrintHello{funprintHelloWorld():Unit{//Redundant"Unit"returntypeprintln("HelloWorld!")}}
Java 的 void
关键字在 Kotlin 里是没有的,取而代之的是一个叫做 Unit
的东西,
Unit 和 Java 的 void
真正的区别在于,void
是真的表示什么都不返回,而 Kotlin 的 Unit
却是一个真实存在的类型:
publicobjectUnit{overridefuntoString()="kotlin.Unit"}
它是一个 object
,也就是 Kotlin 里的单例类型或者说单例对象。当一个函数的返回值类型是 Unit
的时候,它是需要返回一个 Unit
类型的对象的:
funprintHelloWorld():Unit{println("HelloWorld!")returnUnit//returnUnit可以省略}
只不过因为它是个 object
,所以唯一能返回的值就是 Unit
本身。
这两个 Unit
是不一样的,上面的是 Unit
这个类型,下面的是 Unit
这个单例对象,它俩长得一样但是是不同的东西。注意了,这个并不是 Kotlin 给Unit
的特权,而是 object
本来就有的语法特性。如果有需要,也可以用同样的格式来使用别的单例对象,是不会报错的:
包括也可以这样写:
valunit:Unit=Unit
也是一样的道理,等号左边是类型,等号右边是对象——当然这么写没什么实际作用啊,单例可以直接用。
objectZhangsanfungetZhangsan():Zhangsan{//单例可以直接使用returnZhangsan}
因此,在结构上,Unit
并没有任何特别之处,它只是 Kotlin 的 object
。除了对于函数返回值类型和返回值的自动补充之外,它的特殊之处更多地在于语义和用途的角度。它是由官方规定的,用于表示「什么也不返回」的场景的返回值类型。但这只是它被规定的用法而已,本质上它是一个实实在在的类型。在 Kotlin 中,不存在真正没有返回值的函数,所有「没有返回值」的函数实质上的返回值类型都是 Unit,而返回值也都是 Unit 这个单例对象。这是 Unit 和 Java 的 void 在本质上的不同之处。
Unit 去除了无返回值函数的特殊性和有返回值函数之间的本质区别,从而使得很多事情变得更加简单,这种通用性为我们带来了便利。
例子: 函数类型的函数参数虽然不能说Java中的所有函数调用都是表达式,但是可以说Kotlin中的所有函数调用都是表达式。
是因为存在特例void,在Java中如果声明的函数没有返回值,那么它就需要用void来修饰。如:
publicvoidprintHelloWorld(){System.out.println("HelloWorld!");}
因为 void 不是类型,所以 函数printHelloWorld()无法匹配 () -> Unit 函数类型
classVoidTest{funprintHelloWorld1():Unit{//作为参数时,就有函数类型()->Unitprintln("HelloWorld!")}funrunTask(task:()->Any){when(valresult=task()){Unit->println("resultisUnit")String->println("resultisaString:$result")else->println("resultisanunknowntype")}}@Testfunmain1(){valvar1=::printHelloWorld1//()->UnitrunTask(var1)//()->UnitrunTask{"Thisisstring"}//:()->StringrunTask{42}//()->Int}}
现在有了 Unit , fun printHelloWorld1():Unit 作为参数时,就有函数类型 () -> Unit 。
注意:在 Java 当中,Void 和 void 不是一回事(注意大小写),前者是一个 Java 的类,后者是一个用于修饰方法的关键字。如下所示:
publicfinalclassVoid{@SuppressWarnings("unchecked")publicstaticfinalClassTYPE=(Class)Class.getPrimitiveClass("void");privateVoid(){}}
JAVA中Void类是一个不可实例化的占位符类,用来保存一个引用代表Java关键字void的Class对象。它的作用是在反射或泛型中表示void类型。 例如:Map接口的put方法需要两个类型参数,如果我们只需要存储键而不需要存储值,就可以使用Void类作为类型参数
Mapmap=newHashMap<>();map.put("key",null);。
了解了 Unit
和 Unit?
的关系后,可以画出关系图
Nothing 是 Kotlin 所有类型的子类型。 Noting 的概念与 Any? 恰好相反。
Nothing 也叫底类型(BottomType)。
Nothing的源码是这样的:
publicclassNothingprivateconstructor()
可以看到它本身虽然是 public 的,但它的构造函数是 private 的,这就导致我们没法创建它的实例;而且它不像 Unit 那样是个 object:
publicobjectUnit{overridefuntoString()="kotlin.Unit"}
而是个普通的 class;并且在源码里 Kotlin 也没有帮我们创建它的实例。 这些条件加起来,结果就是:Nothing 这个类既没有、也不会有任何的实例对象。 基于这样的前提,当我们写出这个函数声明的时候:
funnothing():Nothing{}
我们可能无法找到一个合适的值来返回,但是在编写代码时,我们必须返回一个值。这种情况下,我们遇到了一个悖论,即必须返回一个值,但却永远找不到合适的返回值
Nothing的作用: 作为函数永远不会返回结果
的提示funnothing():Nothing{throwRuntimeException("Nothing!")}
根据Nothing的特性, Nothing 专门用于抛异常。
publicclassNotImplementedError(message:String="Anoperationisnotimplemented."):Error(message)@kotlin.internal.InlineOnlypublicinlinefunTODO():Nothing=throwNotImplementedError()
从上面这段代码可以看出,Kotin 源码中 throw 表达式的返回值类型是 Nothing。
throw 这个表达式的返回值是 Nothing 类型。而既然 Nothing 是所有类型的子类型,那么它当然是可以赋值给任意其他类型的。 所以表达式思维中的问题就可以解答了
//函数返回值类型是Int,实际上却抛出了异常,没有返回Int//↓↓funcalculate():Int=throwNotImplementedError()
作用二Nothing 类的构造函数是私有的,因此我们无法构造出它的实例。当 Nothing 类型作为函数参数时,一个有趣的现象就出现了:
//这是一个无法调用的函数,因为找不到合适的参数funshow(msg:Nothing){}show(null)//报错show(throwException())//虽然不报错,但方法仍然不会调用
在这里,我们定义了一个 show 函数,它的参数类型是 Nothing。由于 Nothing 的构造函数是私有的,我们将无法调用 show 函数,除非我们抛出异常,但这没有意义。 这个概念在泛型星投影的时候是有应用的,具体后面会解释。
作用三而除此之外,Nothing 还有助于编译器进行代码流程的推断。比如说,当一个表达式的返回值是 Nothing 的时候,就往往意味着它后面的语句不再有机会被执行。如下图所示:
了解了 Nothing 和 Nothing?的关系后,可以画出关系图
平台类型image.png
平台类型在Kotlin中表示为type!(如String!,Int!, CustomClass!)。 Kotlin平台类型本质上就是Kotlin不知道可空性信息的类型,即可以当作可空类型,也可以当作非空类型。平台类型只能来自Java,因为Java中所有的引用都可能为null,而Kotlin中对null有严格的检查和限制。 但是在Kotlin中是禁止声明平台类型的变量的。
image.png
具体的代码示例如下:
//Java代码publicclassPerson{privateStringname;publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.name=name;}}//Kotlin代码funmain(){valperson=Person()//valname=person.name//name是String!类型println(name.length)//可能抛出空指针异常person.name=null//允许赋值为null}
在这个例子中, name 是平台类型,
因为它们来自于 Java 代码。Kotlin 编译器不会检查它们是否为 null,所以需要程序员自己负责。如果要避免空指针异常,可以使用安全调用运算符(?.)或非空断言运算符(!!)来处理平台类型。
println(name?.length)//安全调用,如果name为null则返回nullprintln(name!!.length)//非空断言,如果name为null则抛出异常
平台类型是指 Kotlin 和 Java 的互操作性问题, 在混合项目中要多加注意。
小结Any 是所有非空类型的根类型,而 Any? 才是所有类型的根类型。
Unit 与 Java 的 void 类型相似,代表一个函数不需要返回值;而 Unit? 这个类型则没有太多实际的意义。
当 Nothing 作为函数返回值时,意味着这个函数永远不会返回结果,而且还会截断程序的后续流程。Kotlin 编译器也会根据这一点进行流程分析。
当 Nothing 作为函数参数时,就意味着这个函数永远无法被正常调用。这在泛型星投影的时候是有一定应用的。
Nothing 可以看作是 Nothing? 的子类型,因此,Nothing 可以看作是 Kotlin 所有类型的底类型。
正是因为 Kotlin 在类型系统中加入了 Unit、Nothing 这两个类型,才让大部分无法产生值的语句摇身一变,成为了表达式。这也是“Kotlin 大部分的语句都是表达式”的根本原因。
泛型:让类型更加安全Kotlin 的泛型与 Java 一样,都是一种语法糖,即只在源代码中有泛型定义,到了class级别就被擦除了。 泛型(Generics)其实就是把类型参数化,真正的名字叫做类型参数,它的引入给强类型编程语言加入了更强的灵活性。
泛型的优点类型安全:泛型可以在编译时检查类型,从而避免了在运行时出现类型不匹配的错误。这可以提高程序的可靠性和稳定性。
代码重用:泛型可以使代码更加通用和灵活,从而可以减少代码的重复和冗余。例如,我们可以编写一个通用的排序算法,可以用于任何实现了 Comparable 接口的类型。
在 Java 中,我们常见的泛型有:泛型类、泛型接口、泛型方法和泛型属性,Kotlin 泛型系统继承了 Java 泛型系统,同时添加了一些强化的地方。
泛型接口/类(泛型类型)定义泛型类型,是在类型名之后、主构造函数之前用尖括号括起的大写字母类型参数指定:
声明泛型接口Java:
//泛型接口interfaceDrinks{Ttaste();voidprice(Tt);}
Kotlin:
//泛型接口interfaceDrinks{funtaste():Tfunprice(t:T)}
声明泛型类Java
abstractclassColor{Tt;abstractvoidprintColor();}classBlue{Stringcolor="blue";}classBlueColorextendsColor{publicBlueColor(Blue1t){this.t=t;}@OverridepublicvoidprintColor(){System.out.println("color:"+t.color);}}
Kotlin
abstractclassColor(vart:T/*泛型字段*/){abstractfunprintColor()}classBlue{valcolor="blue"}classBlueColor(t:Blue):Color(t){overridefunprintColor(){println("color:${t.color}")}}
泛型字段定义泛型类型字段,可以完整地写明类型参数,如果编译器可以自动推定类型参数,也可以省略类型参数:
abstractclassColor(vart:T/*泛型字段*/){abstractfunprintColor()}
声明泛型方法Kotlin 泛型方法的声明与 Java 相同,类型参数要放在方法名的前面:
Java
publicstaticTfromJson(Stringjson,ClasstClass){Tt=null;try{t=tClass.newInstance();}catch(Exceptione){e.printStackTrace();}returnt;}
Kotlin
funfromJson(json:String,tClass:Class):T?{/*获取T的实例*/valt:T?=tClass.newInstance()returnt}
泛型约束Java 中可以通过有界类型参数来限制参数类型的边界,Kotlin中泛型约束也可以限制参数类型的上界:
Java
publicstatic>TmaxOf(Ta,Tb){if(a.compareTo(b)>0)returna;elsereturnb;}
Kotlin
fun>maxOf(a:T,b:T):T{returnif(a>b)aelseb}
image.png
where关键字: 多个上界用 whereJava 中多约束: &
publicstatic>Listtest(Listlist,Tthreshold){returnlist.stream().filter(it->it.compareTo(threshold)>0).collect(Collectors.toList());}
Kotin 中多约束:where
//多个上界的情况funtest(list:List,threshold:T):ListwhereT:CharSequence,T:Comparable{returnlist.filter{it>threshold}.map{it}}
所传递的类型T必须同时满足 where 子句的所有条件,在上述示例中,类型 T 必须既实现了 CharSequence 也实现了 Comparable。
泛型形参&泛型实参泛型类:
泛型函数:
泛型的型变不变先看一段 Java 代码,我们知道在Java中 ,List无法赋值给List
publicclassJavaGeneryc{publicstaticvoidmain(String[]args){Listapples=newArrayList<>();apples.add(newApple());Listfruits=apples;//编译错误for(Fruitfruit:fruits){System.out.println(fruit);}}}classFruit{//父类}classAppleextendsFruit{//子类}
image.png
但是到了Kotlin这里我们发现了一个奇怪的现象
funmain2(args:Array){valstringList:List=ArrayList()valanyList:List=stringList//编译成功}
image.png
在Kotlin中竟然能将List赋值给List,不是说好的Kotlin和Java的泛型原理是一样的吗?怎么到了Kotlin中就变了?其实我们前面说的都没错,关键在于这两个List并不是同一种类型。我们分别来看一下两种List的定义:
虽然都叫List,也同样支持泛型,但是Kotlin的List定义的泛型参数前面多了一个 out关键词(加上out 发生协变 ),这个关键词就对这个List的特性起到了很大的作用。 普通方式定义的泛型是不变的,简单来说就是不管类型A和类型B是什么关系,Generic与Generic(其中Generic代表泛型类)都没有任何关系。比如,在Java中String是Oject的子类型,但List并不是List的子类型,在Kotlin中泛型的原理也是一样的。Kotin 使用 out 才发生了变化。
**
out 位置与 in 位置函数参数的类型叫作in位置,而函数返回类型叫作out位置
协变 :保留子类型化关系如果在定义的泛型类和泛型方法的泛型参数前面加上out关键词,说明这个泛型类及泛型方法是协变,简单来说类型A是类型B的子类型,那么Generic也是Generic的子类型,
**
image.png
协变点 (out 位置)函数返回值类型为泛型参数。
协变的特征只能消费,只能取
子类型化会被保留(Producer是Producer的子类型)
T只能用在out位置
image.png
interfaceBookinterfaceEduBook:BookclassBookStore{fungetBook():T{TODO()}}funcovariant(){//教材书店valeduBookStore:BookStore=BookStore()//书店valbookStore:BookStore=eduBookStore//协变,教辅书店是书店的子类型valbook:Book=bookStore.getBook()valeduBook:EduBook=eduBookStore.getBook()}
image.png
协变小结•子类型 Derived 兼容父类型 Base •生产者 Producer 如果在定义的泛型类和泛型方法的泛型参数前面加上in关键词,说明这个泛型类及泛型方法是逆变,简单来说类型A是类型B的子类型,那么Generic是Generic****的子类型,类型父子关系反转。 ** 函数参数类型为泛型参数。 只能生产,只能放入 子类型化会被反转(Consumer是 Consumer的子类型) T只能用在in位置 image.png 垃圾不能扔到干垃圾桶,但是可以扔到垃圾桶。 干垃圾可以扔到垃圾桶,也可以扔到垃圾桶。 由此可以看出垃圾桶可以替代干垃圾桶, 所以干垃圾桶是父类型。 声明为 in ,在 out 位置使用,是会报错的。 image.png 子类型 Derived 兼容父类型 Base 消费者 Consumer兼容 Consumer< Derived> 记忆小技巧: in 表示逆变, in 倒序过来是 ni(逆)。 在Kotlin中out代表协变,in代表逆变,为了加深理解我们可以将Kotlin的协变看成Java的上界通配符,将逆变看成Java的下界通配符: 总的来说,Kotlin 泛型更加简洁安全,但是和 Java 一样都是有类型擦除的,都属于编译时泛型。 下期分享: 星投影 注解 @UnsafeVariance 内联特化(内联强化) reified 标签:
openclassWaste//干垃圾classDryWaste:Waste()//垃圾桶classDustbin
泛型中的out与in与 Java 上下界通配符关系协变 逆变 不变型 Producer Consumer MutableList: 类的子类型化保留了:Producers是 Producer 子类型化反转了:Consumer是 Consumer的子类型 没有子类型化 T只能在out 位置 T只能在 in 位置 T可以在任何位置
小结//Kotlin使用处协变funsumOfList(list:List
Java 泛型 Java 中代码示例 Kotlin 中代码示例 Kotlin 泛型 泛型类型 class Box class Box 泛型类型 泛型方法 T fromJson(String json, ClasstClass) funfromJson(json: String, tClass: Class): T? 泛型函数 有界类型参数 class Box class Box 泛型约束 上界通配符 void sumOfList(List extends Number> list) fun sumOfList(list: List) 使用处协变 下界通配符 void addNumbers(List super Integer> list) fun addNumbers(list: List) 使用处逆变
最新文章推荐
- 焦点短讯!From Java To Kotlin 2:Kotlin 类型系统与泛型
- 热资讯!苹果“上新”:15英寸MacBook Air 10499元起,你会买吗?
- 零钱理财是什么意思 情况是这样的
- 【环球时快讯】RTX4060游戏本重回五千价位段 附性能测试和机型推荐
- 盲山的原型_盲山真实人物原型现况
- 三非人员指的是什么_三非指的是什么
- 每日消息!董大庆_关于董大庆的简介
- 黄色的西红柿的营养价值_西红柿的营养价值 速看料
- 中消协、中装协专项监督“野蛮装修”,消费者咨询邮箱公布
- 天天关注:美国一男子吃麦当劳_居然减重53斤 垃圾食品也有这疗效 咋做到的
X 关闭
资讯中心
2022-05-20
2021-10-18
2021-10-18
2021-10-18
X 关闭
热点资讯
-
1
杏花绽放催热“赏花经济” 吸引了大量游客前来旅游
-
2
上海籍阳性夫妻内蒙古密接、次密接者出现初筛阳性情况
-
3
内蒙古二连浩特:市民非必要不出小区、不出城
-
4
重庆一名潜逃24年的持枪抢劫嫌犯落网
-
5
销售有毒、有害食品 郭美美获刑二年六个月
-
6
陕西新增6名确诊病例1名无症状感染者 西安全面开展排查管控
-
7
《加强建设中国风湿免疫病慢病管理》倡议书:建立基层医院独立风湿科
-
8
游客因未购物遭导游辱骂?九寨沟:相关部门已介入调查
-
9
郭美美再入狱!销售有毒有害食品获刑2年6个月
-
10
2020年黄河青海流域冰川面积和储量较十年前缩减
-
11
5名“摸金校尉”落网 内蒙古警方破获一起盗掘古墓葬案