学习Kotlin之泛型和委托
泛型
泛型的基本用法
泛型主要有两种定义方式:一种是定义泛型类,另一种是定义泛型方法,使用的语法结构都是<T>。
定义一个泛型类:
class MyClass<T> { fun method(param: T): T { return param } }
此时的MyClass就是一个泛型类,MyClass中的方法允许使用T类型的参数和返回值。
调用的时候写法如下:
val myClass = MyClass<Int>()
val result = myClass.method(123)
定义一个泛型方法:
class MyClass { fun <T> method(param: T): T { return param } }
调用方式也需要进行相应的调整:
val myClass = MyClass()
val result = myClass.method<Int>(123)
由于Kotlin的类型推导机制,我们传入的是Int类型的参数,它能自动推导出来,所有可以省略泛型的指定:
val result = myClass.method(123)
泛型类型限制
目前method()方法的泛型指定成任意类型,还可以通过指定上界的方式来对泛型的类型进行约束,比如这里将method()方法的泛型上界设置为Number类型,如下所示:
class MyClass { fun <T : Number> method(param: T): T { return param } }
表示method方法的泛型只能指定为Int,Float,Double等。
另外,默认情况下,所有的泛型都可以指定为可空类型。因为在不指定上界的时候,泛型的上界默认是Any?。如果想要让泛型的类型不可为空,只需要将泛型的上界手动指定成Any就可以了。
委托
类委托和委托属性
委托是一种设计模式,它的基本理念是:操作对象自己不会去处理某段逻辑,而是会把工作委托给另外一个辅助对象去处理。Java对于委托并没有语言层级的实现。
类委托
核心思想在于将一个类的具体实现委托给另一个类去完成。
Set是一个接口,如果要使用它,需要使用它具体的实现类,比如HashSet。而借助于委托模式,我们可以轻松实现一个自己的实现类。
比如定义一个MySet,并让它实现Set接口,代码如下所示:
class MySet<T>(val helperSet: HashSet<T>) : Set<T> { override val size: Int get() = helperSet.size override fun contains(element: T) = helperSet.contains(element) override fun containsAll(elements: Collection<T>) = helperSet.containsAll(elements) override fun isEmpty() = helperSet.isEmpty() override fun iterator() = helperSet.iterator() }
MySet的构造函数中接收了一个HashSet参数,这就相当于一个辅助对象。然后在Set接口所有的方法实现中,我们都没有进行自己的实现,而是调用了辅助对象中相应的方法实现,这其实就是一种委托模式。
这样做的好处在于:如果我们只是让大部分的方法实现调用辅助对象中的方法,少部分的方法实现由自己来重写,甚至加入一些自己独有的方法,那么MySet就会成为一个全新的数据结构类,这就是委托模式的意义所在。
但是有一个弊端:如果接口中待实现方法很多,那么每个都要去调用辅助对象中方法实现。但是Kotlin可以通过类委托的功能来解决。
Kotlin中委托使用的关键字是by,我们只需要在接口声明的后面使用by关键字,再接上受委托的辅助对象,就可以免去之前所写的一大堆模板式的代码了,如下所示:
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet{ }
如果我们要对某个方法进行重新实现,只需要单独重写那一个方法就可以了,如下所示:
class MySet<T>(val helperSet: HashSet<T>) : Set<T> by helperSet{ fun helloWorld() = println("Hello World") override fun isEmpty() = false }
现在MySet就是一个全新的数据结构类,不仅有新方法,还有HashSet中的功能。这就是Kotlin的类委托。
委托属性
委托属性的核心思想是将一个属性(字段)的具体实现委托给另一个类去完成。
委托属性的语法结构,如下所示:
class MyClass { var p by Delegate() }
by关键字连接了左边的p属性和右边的Delegate实例。这种写法就代表着将p属性的具体实现委托给了Delegate类去完成。
当调用p属性的时候会自动调用Delegate类的getValue()方法,当给p属性赋值的时候会自动调用Delegate类的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 } }
这是一种标准模板,在Delegate类中我们必须实现getValue()和setValue()这两个方法,并且都要使用operator关键字进行声明。
getValue()方法接收两个参数:
第一个: 用于声明该Delegate类的委托功能可以在什么类中使用,这里写成MyClass表示仅可在MyClass类中使用;
第二个: KProperty<*>是Kotlin中的一个属性操作类,可用于获取各种属性相关的值,在当前场景下用不着,但是必须在方法参数上进行声明。
<*>这种泛型的写法表示不知道或者不关心泛型的具体类型,只是为了通过语法编译而已。
setValue()方法也是相似的,只不过它要接收3个参数:
前两个参数和getValue()方法是相同的;
最后一个参数表示具体要赋值给委托属性的值,这个参数的类型必须和getValue()方法返回值的类型保持一致。
整个委托属性的工作流程就是这样实现的,现在当我们给MyClass的p属性赋值时,就会调用Delegate类的setValue()方法,当获取MyClass中p属性的值时,就会调用Delegate类的getValue()方法。
委托功能本身不难理解,真正的难点在于如何灵活地进行应用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异