目录

Kotlin基础 泛型,类委托,infix

泛型

Kotlin中的泛型和Java中的泛型有同有异.

泛型主要有两种定义方式:

  1. 定义泛型类
  2. 定义泛型方法

例如:

// 定义泛型类
class MyClass<T>{
    fun method(param: T):T{
        return param
    }
}

// 定义泛型方法
class MyClass{
    fun <T> method(param: T):T{
        return param
    }
}

泛型的界限限定

如果想指定泛型只能是数字那么<T : Number>.这样泛型的上界设置就为Number类型.

默认情况下, 所有的泛型都可以指定为可空类型, 因为在不手动设置上界时, 默认都是Any?

泛型的实化

概念: 在Java中的泛型功能是通过类型擦除机制来实现的. 就是泛型的约束只存在编译时期 运行时仍然会按照JDK1.5之前机制运行, JVM是识别不出来我们在代码中指定的泛型类型. 所以像T.class这样语法是无法工作, 因为运行时实际类型已经被擦除.

但是Kotlin提供了内联函数的概念, 内联函数就是会在编译的时候自动替换它调用的地方,这样也就不存在泛型擦除的问题, 因为代码在编译之后会直接使用实际类型来替代内联函数中的泛型声明.

所以泛型实化写法:

  1. 函数必须是内联函数
  2. 声明泛型的地方必须加上reified关键字, 表示该泛型可以实例化

例如:

inline fun <reified T> getType() = T::class.java

这行代码实现了java中完全不可能实现的功能: 函数直接返回了当前指定泛型的实际类型.

类委托和委托属性

委托是一中设计模式, 基本理念: 操作对象自己不会去处理某段逻辑, 而是会把工作委托给另外一个辅助对象去处理. Java中对于委托并没有语言层级的实现, 而C#等语言就对委托进行了原生的支持.

Kotlin中也支持委托功能, 并且将委托分为了两种: 类委托和属性委托.

类委托

核心思想就是将一个类的具体实现委托给另一个类.

为什么要委托? 我们想保留一个类的大部分方法, 少部分方法自己重写, 或者加入一些自定义方法, 这就是委托的意义. 但是如果如果要委托的是个接口, 并且接口有很多, 手动实现属实很麻烦, Kotlin中可以通过类委托来解决.

关键字: by

例如:

class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet{}

by后面接上受委托的辅助对象

属性委托

class MyClass{
    var p by Delegate()
}

委托属性核心思想是将一个属性字段的具体实现委托给另一个类去完成.

p属性的具体实现委托给了Delegate类去完成, 当调用p属性时会自动调用Delegate类的getValue(), 赋值时会调用setValue()

因此还需要对Delegate类进行具体实现.

class Delegate{
    var propValue: Any? = null
    
    operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any?{
        return propValue
    }
    
    operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?){
      propValue = value
    }

}
  • getValue(): 第一个参数声明该Delegate类可以在什么类中使用, 这里的写法表明只能在MyClass中使用. 第二个参数时Kotlin的一个属性操作类, 可用于获取各种属性相关的值. <*>表示你不知道或者不关心泛型的具体类型
  • setValue(): 前两个与前面都相似, 最后一个参数表示具体要赋值给委托属性的值, 这个参数的类型必须和getValue()返回值保持一致.

Kotlin中的懒加载by lazy就是利用了委托

infix函数

类似A to B 这种构建键值对的语法. to并非是关键字, 而是Kotlin实现的一个语法糖.

infix函数主要是把编程语言函数的调用语法调整了一下. 例如A to B等价于A.to(B).

例如: 包装String#startsWith()

infix fun String.beginsWith(prefix: String) = startsWith(prefix)

给String类添加一个扩展函数, 判断字符串是否以参数字符串开头. 使用infix后就可以如下调用:

val bol = "Hello World" beginsWith "Hello"

infix 有两个限制:

  1. infix 函数不能定义成顶层函数, 它必须是某个类的成员函数, 可以使用扩展函数的方式将它定义到某个类当中
  2. infix 函数必须接收且只能接收一个参数, 至于参数类型是没有限制的.