Kotlin从入门到精通(抛弃)二
三.Kotlin中的协变和逆变
前面咱们已经学习到了Kotlin的非空判别与函数,今日学一点高档语法,协变和逆变,说到协变和逆变,就不得不说Java中的泛型了,假如你对Java中的泛型很熟悉,那么你很快就能掌握逆变(in –super)和协变(out–extends),所以咱们先来说说java中的泛型吧。
1.理解java中的泛型
什么是泛型?泛型便是编译时存在的,运行时是有类型擦除的,咱们在java中通常用K V E 等来表示泛型,举个比如吧,咱们有一个箱子,咱们放生果到里边去,咱们能够放香蕉、放苹果、放梨子、放西瓜等等。可是当咱们取的时分用什么样的盘子来接纳呢?会不会拿个小碗来装西瓜呢,那不是放不下了吗!!!所以,就有了泛型,泛型便是指定箱子里边只放某种生果,取的时分也只用特定的盘子来接纳,这样就不会引起放不下的问题了,可是它也有限制的,举个别的的比如吧,人是动物的一种,山君也是动物的一种,可是能装动物的箱子,纷歧定能装人,乃至装山君,这个便是虽然泛型的参数类型可能是基类和拓展类的关系,可是泛型并不支撑,那怎样才干支撑呢?一般情况下需求用到通配符?加上spuer 关键字来限制它的下限必须是动物类型,这样咱们就能够用动物来接纳放进来的人或山君,为什么呢?其实在java中它是一个成下转型(子给父),由于super限制了box中存放的是动物的父类目标,所以咱们放人或许山君进去,它都能够接纳,它是一种类型安全的转化,可是,假如咱们用?+exnteds的话来限制它的上限是动物的话,咱们只能给这个箱子里取东西,为什么呢?由于上限是动物,有可能是塞了人或许山君了呢,所以咱们只能用Animal来接纳取出来的目标,它也是个父接纳子的过程,所以它是类型安全的,需求留意的是,别和上面的泛型承继搞混了哟。所以上面这些话用代码来描述怎样写呢?下面就以代码的形式充分的解释:
首先咱们界说一个父类Animal,以及其子类人和山君
class Animal{ } class Human extends Animal{ } class Tiger extends Animal{}
咱们在试着用装Animal的箱子来装人或山君
却发现它在编译器就提示咱们,咱们需求的是Animal,并不是Human,这个就印证了我前面说的,泛型是没有承继这样的写法的。那咱们现在便是想限制箱子里装Animal,可是我又能够塞Human,那咋办呢?当然便是? extends Animal,可是这个时分只能取,并不能存,为啥呢?你要是塞了个山君进去咋办?所以现在只能取用Animal来接纳塞进去的,同样的,当咱们用super来界说时,就能够塞进去人或山君了,由于里边至少是Animal的父类,当然能够承受子类了。咱们类比下Kotlin的in 和out,其实便是差不多的概念, <out T>
只能作为消费者,只能读取不能增加,理解为? extends T
,而<in T>
只能作为生产者,只能增加不能读取,类似于? super T
,这个在java中又称为PECS原则,下面为代码展现:
/**
* 不能够存,只能够用Animal来接纳,为什么呢?你在界说的时分是人
* 假如你塞了个山君咋办,所以不支撑塞,可是你用Animal去取便是下转型了,
* 它是类型安全的
* List<? extends Animal> list=new ArrayList<Human>();
* out 等同于? extends
*/
//
val mutableList: MutableList<out View> = mutableListOf<Button>()
/**
* 能够存,可是不可取,为什么呢?由于实际类型至少Animal的父类,所以能够承受子类类型
* 可是取的话,无法确定具体存放的类型,会存在类型转化,只能有object来接纳
* List<?super Human> list1=new ArrayList<Animal>();
* in 等同于? super
*/
val mutableList2: MutableList<in Button> = mutableListOf<View>()
2.Kotlin中逆变和协变
Kotlin中的*类似于java中的?,咱们在限制参数的上界时 在java中用extends 在Kotlin中运用:假如是多重上界的话 在java中运用的是&符号,而在Kotlin
中运用的。需求拿出来而且用where,举个比如
interface A{} interface B{ } interface C<T> where T:A ,T:B{ }
,这个在Java中的写法便是interface C<T extends A&b>{}
小结
Kotlin的泛型并不难,条件是你对java中的泛型比较了解,知道什么是PECS,也就明白了out(逆变)和in(协变),当然java中还有类型擦除等等知识点,以及泛型类、泛型接口、泛型办法等,这个就不深化的去写了。
四.Kotlin中的静态与常量
其实很简单
咱们知道在java中运用静态变量,运用关键字static就能够了,可是在kotlin中,一般会运用object关键字,那这样写呢?咱们新建一个叫Constant的object,把需求作为全局的静态变量写在里边即可,像下面这样:
object Constant{ var name="abc" var pwd="efg"}
,咱们就能够在其他当地经过Constant.来取值了,当然咱们假如在class中也是声明一个object的,可是一般情况下,假如在类中声明object,咱们可能需求运用compain object(伴生目标)而且能够省略object的变量名,这样在外部也能够运用类.变量名来取值,对了,一般情况下,咱们在object中声明一个val的变量,他都会提示咱们加上const,那const又有啥用呢?咱们反编译代码后,能够看出来const润饰的会变成java中的public,而一般的仅仅private,那这个又有啥呢?假如你在java中调用可能需求运用get来获取未被const润饰的常量了,别的const一般都只润饰object中的常量和顶层等级的常量,而且根据提示只能是Const ‘val’ has type ‘Af’. Only primitives and String are allowed才干够增加哟,对了说到了顶层等级,一般也便是文件,咱们一般会在其中写一些办法工具或许是扩展函数。
小结
好像没什么好总结的,都是零星的知识点… “_“`~!
五.密封类、枚举、数据类、运算符重载
1.代数数据类型
编辑器竟然提示我,枚举的举是个错别字,哈哈哈,好了,言归正传,先说密封类,说起密封类,我就要好好的聊聊了,究竟很久之前被面试官问过,其时也是预备不足,回答的不够好(烂),那密封类到底是啥,那就要先了解下枚举,枚举在java中便是一个单例,比较于静态变量来限制类型或许状态,它的限制性更强,也便是代数数据类型(Algebraic Data Type),而密封类是为了解决枚举中的一些缺点,假如给枚举中的类型增加特点,就需求给一切的枚举类型增加,而密封类能够在里边独自写一个类型承继父类型并独自增加特点。
2.Kotlin中的数据类与运算符重载
它是存储数据的目标,没有什么实际意义,运用data 关键字,它会自动生成toString, equals hashcode等,运用它时需求留意的点,便是不能用inner abstract sealed 润饰,而且在主构造函数中至少有一个特点,还有哦,它默认支撑解构语法,而且自带copy,不过copy好像仅仅会运用主构造函数。运算符重载,也就一些函数名能够运用一些符号界说,而且这些运算符重载不能自界说,下面咱们是咱们常见的Kotlin的运算符重载:
操作符 | 函数名 | 效果 |
---|---|---|
+ | plus | 把一个目标加到别的一个目标里边去 |
+= | plusAssign | 把一个目标增加到别的一个目标中去,而且赋值给第一个目标 |
== | equals | 判别两个目标是否持平 |
[] | get | 获取调集中的目标 |
… | rangeTo | 创立一个range目标 |
in | contains | 调集中是否包含某个目标 |
小结
好像没什么好总结的,都是零星的知识点… “_“`~!
总结
还是总结一下吧,这篇文章(水文)首要讲了Kotlin的协变和逆变,咱们先从java的PECS开始讲起,然后等同于Kotlin的out(produce) 和 in(consumer),然后咱们经过静态变量来对object的关键字进行了了解,那它的用法首要有三个:1.单列的类;2.单例目标;3.伴生目标;最后咱们对代数数据类型枚举和密封类做了一些讲解,并对比了它们的不同,最后咱们又对数据类和运算符重载这个小知识点做了简单的介绍,over!!!码字不易,假如觉得此文对你有用,欢迎动动小手沟通沟通。