接口定义
Kotlin 规定所有的接口属性和函数实现都要使用 override 关键字,接口中定义的函数并不需要 open 关键字修饰,他们默认就是 open 的。
示例:
接口的默认实现
只要愿意,你可以在接口里提供默认属性的 get() 方法和函数实现。
注意:我们在接口里定义属性的情况不是很多。接口里面一般只提供功能函数的声明。
要定义一个抽象类,你需要在定义之前加上 abstract 关键字,除了具体的函数实现,抽象类也可以包含抽象函数---只有定义,没有函数实现。
注意:抽象类里既可以有实现了的函数,也可以有没实现的函数;接口里只能有没实现的函数
泛型类的构造函数可以接受任何类型。
MagicBox 类指定的泛型参数由放在一对<>里的字母 T 表示,T 是个代表 item 类型的占位符。MagicBox 类接受任何类型的 item 作为主构造函数值(item : T),并将 item 值赋给同样是 T 类型的 subject 私有属性。
注意:泛型参数通常用字母 T(代表英文 type)表示。当然,想用其他字母,甚至是英文单词都是可以的。不过,其它支持泛型的语言都在用这个约定俗成的 T,所以建议你继续用它,这样写出的代码别人更容易理解。
泛型函数
泛型参数也可以用于函数。
定义一个函数用于获取元素,当且仅当 MagicBox 可用时,才能获取元素。即上面代码设置 available 为 true。因为在 fetch() 方法中,takeIf{} 里面的表达式为 true 时,takeIf 返回接收者对象,即这里的泛型类型 magicBoy。
多泛型参数
泛型函数或泛型类也可以有多个泛型参数。
class MagicBox<T>(item : T){
var available : Boolean = false
private val subject : T = item
// 泛型函数
fun fetch() : T? {
return subject.takeIf { available }
}
// return --> R。TODO 多泛型参数
// 业务,把元素进行修改。魔盒里面放的是男孩,取出来时,给他改成一个男人
fun <R> fetch(subjectModFunction : (T) -> R) : R?{
return subjectModFunction(subject).takeIf { available }
}
}
class Boy(val name : String, val age : Int)
class Man(val name : String, val age : Int)
class Dog(val weight : Int)
fun main() {
val magicBoy : MagicBox<Boy> = MagicBox(Boy("jack", 17))
val magicDog : MagicBox<Dog> = MagicBox(Dog(10))
magicBoy.available = true
magicBoy.fetch()?.run {
println("you find $name")
}
// 得到一个 man
val man : Man? = magicBoy.fetch {
Man(it.name, it.age.plus(15))
}
}
泛型类型约束
如果要确保 MagicBox 里面只能装指定类型的物品,比如 Human 类型,怎么办?
?vararg 关键字
MagicBox 能存放任何类型的 Human 实例,但一次只能放一个 T 类型实例。如果需要放入多个 T 类型实例,那么就需要使用到 vararg 关键字。
out-协变
out(协变),如果泛型类只将泛型类型作为函数的返回(输出),那么使用 out,可以称之为生产类/接口,因为它主要是用来生产(produce)指定的泛型对象。
示例:
// 生成者
interface Production<out T>{
//TODO 泛型类型 仅作为 函数的返回 --> 用 out
fun product() : T
}
open class Food()
open class FastFood() : Food()
class Burger() : FastFood()
class FoodStore() : Production<Food>{
override fun product(): Food {
println("Food Store!")
return Food()
}
}
class FastFoodStore() : Production<FastFood>{
override fun product(): FastFood {
println("Fast Food Store!")
return FastFood()
}
}
class BurgerStore() : Production<Burger>{
override fun product(): Burger {
println("Burger Store!")
return Burger()
}
}
fun main() {
val foodStore : Production<Food> = FoodStore()
/**
* FastFoodStore() 本来就是继承自 Production<FastFood>,而这里的接收对象为fastFoodStore2的类型Production<Food>
* FastFood 是 Food 的子类
* 这种情况在 Java 里是 子类泛型对象 不能赋值给 父类泛型对象
* 但是在 kotlin 里,因为有 out 所以就可以这样做
*/
val fastFoodStore1 : Production<FastFood> = FastFoodStore()
val fastFoodStore2 : Production<Food> = FastFoodStore()
}
in-逆变
in(逆变),如果泛型类只将泛型类型作为函数的入参(输入),那么使用 in,可以称之为消费者类/接口 ,因为它主要是用来消费(consume)指定的泛型对象。
// 消费者
interface Consumer<in T>{
//TODO 泛型类型 仅作为 函数的入参 --> 用 in
fun consume(item : T)
}
open class Food()
open class FastFood() : Food()
class Burger() : FastFood()
class Everybody : Consumer<Food>{
override fun consume(item: Food) {
println("Eat food $item")
}
}
class ModernPeople : Consumer<FastFood>{
override fun consume(item: FastFood) {
println("Eat fastFood $item")
}
}
class American : Consumer<Burger>{
override fun consume(item: Burger) {
println("Eat Burger $item")
}
}
fun main() {
val everybody1 : Consumer<Food> = Everybody() // 正常情况
//TODO in : 父类泛型 对象赋值给 子类泛型对象
val everybody2 : Consumer<FastFood> = Everybody()
val everybody3 : Consumer<Burger> = Everybody()
val modernPeople : Consumer<Burger> = ModernPeople()
val american : Consumer<Burger> = American()
}
invariant-不变
如果泛型类既将泛型类型作为函数参数,又将泛型类型作为函数的输出,那么即不用 out 也不用 in。
什么时候使用 in 和 out
1. 父类泛型对象可以赋值给子类泛型对象,用 in。
2. 子类泛型对象可以赋值给父类泛型对象,用 out。
reifield
有时候,你可能想知道某个泛型参数具体是什么类型,reified 关键字能帮你检查泛型参数类型。Kotlin 不允许对泛型参数 T 做类型检查,因为泛型参数类型会被类型擦除,也就是说,T 的类型信息在运行时是不可知的,Java 也有这样的规则。