接口、抽象类、泛型,这些在Java中我们都是常见且经常使用的,相信大家都不会陌生了。这里我们就看下它们的异同吧。
一 接口
Kotlin规定所有的接口属性和函数实现都要使用override关键字,接口中定义的函数并不需要open关键字修饰,他们默认就是open的。
1.1 接口的定义及实现
interface Movable{
val maxSpeed:Int
var wheels:Int
fun move(movable: Movable):String
}
class Car(_name:String,override var wheels:Int = 4):Movable{
var name = _name
override val maxSpeed: Int
get() = 100
override fun move(movable: Movable): String {
return "$name max speed is ${movable.maxSpeed},has ${movable.wheels} wheels"
}
}
val car = Car("BMW",8)
println(car.move(car))
//结果
BMW max speed is 100,has 8 wheels
1.2 接口默认实现
interface Movable{
val maxSpeed:Int
get() = (1..500).shuffled().last()
var wheels:Int
fun move(movable:Movable):String
}
class Car(var name:String,override var wheels:Int = 4):Movable{
override val maxSpeed: Int
get() = super.maxSpeed
override fun move(movable: Movable): String {
return "$name has $wheels wheel,max speed is ${movable.maxSpeed}"
}
}
这里的默认实现只是在接口里默认实现了属性取值,然后实现类可以直接取父接口的属性值。总体来看Kotlin的接口使用与Java差异不太大。
二 抽象类
要定义一个抽象类,你需要在定义之前加上abstract关键字,除了具体的函数实现,抽象类也可以包含抽象函数(只定义不实现)。
2.1 抽象类定义及实现
abstract class Gun(val range:Int){
abstract fun trigger():String
}
class AK47(val price:Int,range:Int):Gun(range = range){
override fun trigger(): String {
return "AK47 price is $price,shooting $range"
}
}
val aK47 = AK47(9999,108)
println(aK47.trigger())
//结果
AK47 price is 9999,shooting 108
感觉没什么好多解释的了,除了一些写法不一样,其余与Java基本一致,看代码理解下就可以了。
三 泛型
泛型通常用字母T(Type)表示,要是想用其它字母甚至是英文单词都是可以的。不过在其它支持泛型的语言通常都使用T,慢慢的成了一个约定俗成。为了让别人更容易理解你的代码,也建议使用T。
3.1 定义泛型类
泛型类的构造函数可以接受任何类型
class MagicBox<T>(item:T){
var subject:T = item
}
data class Boy(val name:String,val age:Int)
data class Dog(val weight:Int)
val box0 = MagicBox(Boy("Jack",18))
val box1 = MagicBox(Dog(20))
3.2 泛型函数
在上面例子的基础上我们将磨合改一改
class MagicBox1<T>(item:T){
var availavle = true
private var subject:T = item
fun fetch():T?{
return subject.takeIf { availavle }
}
}
val box0 = MagicBox1(Boy("Jack",18)).fetch()
val box1 = MagicBox1(Dog(20)).fetch()
println(box0.toString())
println(box1.toString())
//结果
Boy(name=Jack, age=18)
Dog(weight=20)
嘿嘿,是不是感觉很奇怪,明明直接实例化就好了,还搞得那么麻烦。其实这样写也是有其好处的,可以方便统一实例化工作的管理,在Android源码里就使用了很多这样的构建方式,也是一种构建思想了。当然,它是用Java写的而已。
3.3 多泛型参数
泛型函数或泛型类也可以有多个泛型参数。感觉很绕,无法理解。那么我们通俗点,比如我们开始传入的是一个男孩,然后最后我们需要得到一个男人怎么处理?这个要是你玩过RxJava的话,应该是处理过类似的需求的。
class MagicBox2<T>(item:T){
var availavle = true
private var subject:T = item
fun fetch():T?{
return subject.takeIf { availavle }
}
fun <R>fetch(subjectModFunction:(T)->R):R?{
return subjectModFunction(subject).takeIf { availavle }
}
}
data class Man(val name:String,val age:Int)
val box2 = MagicBox2(Boy("Jack",18)).fetch{
Man(it.name,it.age.plus(10))
}
println(box2.toString())
//结果
Man(name=Jack, age=28)
在变换的过程中一定有函数的介入处理的,但事先我们并不知道它的具体类型,所以有了T、R两个泛型,然后通过外部传入lambda表达式来进行了转换。这种处理方式在实际项目中是会经常用到的。
3.4 泛型约束
如果要确保我们的泛型只能是某种类型的泛型该怎么处理呢?那么我们需要对我们的泛型进行条件约束。
class MagicBox3<T:Man>(item:T){
var availavle = true
private var subject:T = item
fun fetch():T?{
return subject.takeIf { availavle }
}
}
//这个是非法的,不符合约束条件
//val box3 = MagicBox3(Boy("Jack",18))
//这句代码就没问题了
val box4 = MagicBox3(Man("Jack",18))
其实类似的用法我们在Java中也应该写过,就是对泛型进行约束,要求只能是某种类型的子类。Java的写法是 T extend Man 这种写法而已。
3.5 可变参数泛型
我们设计的程序一次只能放入一个,但如果我一次想放入N个,但又可能不确定,那这时候我们的可变参数就派上用场了。可变参数使用vararg关键字。
class MagicBox4<T>(vararg item:T){
var availavle = true
private var subject:Array<out T> = item
fun fetch(index:Int):T?{
return subject[index].takeIf { availavle }
}
}
val box5 = MagicBox4(Boy("Jack",18),Dog(20),Man("Jacky",30))
println(box5.fetch(1))
//结果
Dog(weight=20)
这里需要注意的点:1、需要使用vararg关键字指明参数为可变参数 2、subject接收的类型变为了Array,且类型需要使用out进行修饰(out in在下节在说) 3、取值的时候需要下标做取值索引
3.6 []取值操作符
在上面的代码中,如果我们想通过box5[1]这种方式进行取值是行不通的。但我们可以通过重载运算符函数get来实现此功能。
class MagicBox4<T>(vararg item:T){
var availavle = true
private var subject:Array<out T> = item
fun fetch(index:Int):T?{
return subject[index].takeIf { availavle }
}
operator fun get(index: Int):T? = subject[index].takeIf { availavle }
}
通过重载get运算符函数,我们就可以通过[]直接取值了。