Kotlin语法(十)-扩展
2016-09-03 09:57
162 查看
参考原文:http://kotlinlang.org/docs/reference/extensions.html
类似于C#和Gosu,Kotlin提供了不用继承父类,或者使用像装饰模式这样的设计模式方式来给某个类进行扩展功能(Extensions)。
扩展类型支持:函数和属性。
添加扩展函数后,就可以对“MutableList<Int>”实例使用扩展的函数:
调用扩展功能,调用的是实例定义类型对应的扩展,而不是实例的实际类型对应的扩展。
实际传入的参数为D的实例,但最后调用的是C的扩展函数,因为在定义时参数对应的类型为C。
类有一个函数,再给该类定义一个相同名称的扩展函数,则该类的实例调用的为类原有的函数。
若是重载(函数名称相同,参数不同)方式扩展一个函数,则根据参数调用到不同的函数:
下面的实例,可以不用判定实例是否为空,就直接调用“toString()”函数,在该扩展函数内部进行为空判定。
扩展的属性不是在原类中增加相关属性:
Ø 扩展属性没有“backing field”
Ø 扩展属性不支持默认初始化;需要明确定义“ getters/setters”
那么在该包的外面使用该扩展,需要在使用时导入该扩展:
另外,若定义扩展的外部类及扩展的类有相同的成员时,默认会调用扩展类的成员,若需要调用定义扩展的外部类的成员,可以通过“this@ClassName”方式:
类中的扩展函数,可以定义为“
对调用扩展的实际类根据参数定义类型调用对应的类的扩展;而调用对象则是根据该对象对应的类实现的扩展。
这种方式在使用的时候,就感觉不是很流畅,本来需要交换list中的i j两个位置的值,而使用方式确是将[list, i, j]三个参数传入到一个方法中。
比较流畅的方式应该是list. swap(i, j),本身该操作对应list;但是很多时候API已经设计好或者不能实现所有的可能的方法;这时候可以通过扩展方式来灵活实现需要的功能。
类似于C#和Gosu,Kotlin提供了不用继承父类,或者使用像装饰模式这样的设计模式方式来给某个类进行扩展功能(Extensions)。
扩展类型支持:函数和属性。
扩展函数
下面方式进行函数扩展:swap为扩展的函数。funMutableList<Int>.swap(index1: Int, index2: Int) { val tmp = this[index1] // 'this' correspondsto the list this[index1] = this[index2] this[index2] = tmp } |
添加扩展函数后,就可以对“MutableList<Int>”实例使用扩展的函数:
val tempM = mutableListOf(1, 2, 3) //[1, 2,3] tempM.swap(0,1) //[2, 1, 3] |
扩展是一种静态方式(Extensions are resolved statically)
扩展实际没有修改扩展类;当定义一个扩展,并没有增加一个成员到扩展类中,仅仅是在扩展类实例可以通过“.”方式使用该扩展功能。调用扩展功能,调用的是实例定义类型对应的扩展,而不是实例的实际类型对应的扩展。
open class C class D: C() fun C.foo() ="c" fun D.foo() ="d" fun printFoo(c: C){ println(c.foo()) } printFoo(D()) //result is “c” |
类有一个函数,再给该类定义一个相同名称的扩展函数,则该类的实例调用的为类原有的函数。
class C { fun foo() { println("member") } } fun C.foo() {println("extension") } val c = C() c.foo() //result is “member” |
class C { fun foo() { println("member") } } fun C.foo(i: Int) {println("extension") } val c = C() c.foo(1) //result is “extension” |
可空接收器(Nullable Receiver)
扩展可以定义一个可空的接收类型;该种扩展可以使用一个null对象调用,可以在扩展函数中使用“this == null”来检测为空状态。
下面的实例,可以不用判定实例是否为空,就直接调用“toString()”函数,在该扩展函数内部进行为空判定。
fun Any?.toString(): String { if (this == null) return "null" // after the null check, 'this' is autocastto a non-null type, so the toString() below // resolves to the member function of theAny class return toString() } |
扩展属性(Extension Properties)
Kotlin支持扩展属性,跟扩展函数语法类型,直接在类名后通过“.”方式添加扩展:val <T>List<T>.lastIndex: Int get() =size - 1 |
Ø 扩展属性没有“backing field”
Ø 扩展属性不支持默认初始化;需要明确定义“ getters/setters”
valFoo.bar = 1 // error: initializers are not allowed for extension properties |
友元(伴侣)对象扩展(Companion Object Extensions)
静态属性(Kotlin中去掉了static,用companion object{ … }方式定义静态函数及属性)的扩展:若一个类中已经有定义“companion object”,则可以对该类“companion object”进行扩展:class MyClass { companion object { } // will be called "Companion" } funMyClass.Companion.foo() { // ... } //直接使用类名调用扩展 MyClass.foo() |
扩展范围(Scope of Extensions)
一般都是在顶层(top level)定义扩展,如:package foo.bar fun Baz.goo() { ... } |
packagecom.example.usage import foo.bar.goo// importing all extensions by name "goo" // or import foo.bar.* // importing everything from"foo.bar" fun usage(baz: Baz){ baz.goo() } |
作为成员扩展(Declaring Extensions as Members)
在一个类中,可以为另外一个类添加扩展。在这样的一个扩展中,可以直接调用外部类的成员。class D { fun bar() { ... } } class C { fun baz() { ... } fun D.foo() { bar() // calls D.bar baz() // calls C.baz } fun caller(d: D) { d.foo() // call the extension function } } |
另外,若定义扩展的外部类及扩展的类有相同的成员时,默认会调用扩展类的成员,若需要调用定义扩展的外部类的成员,可以通过“this@ClassName”方式:
class C { fun D.foo() { toString() // calls D.toString() this@C.toString() //calls C.toString() } |
类中的扩展函数,可以定义为“
open”,子类可以重写该扩展。
对调用扩展的实际类根据参数定义类型调用对应的类的扩展;而调用对象则是根据该对象对应的类实现的扩展。
open class D { } class D1 : D() { } open class C { open fun D.foo() { println("D.foo in C") } open fun D1.foo() { println("D1.foo in C") } fun caller(d: D) { d.foo() // call the extension function } } class C1 : C() { override fun D.foo() { println("D.foo in C1") } override fun D1.foo() { println("D1.foo in C1") } } C().caller(D()) // prints "D.foo in C" C1().caller(D()) // prints "D.foo in C1" - dispatch receiver is resolved virtually C().caller(D1()) // prints "D.foo in C" - extension receiver is resolved statically C1().caller(D1()) // prints "D.foo in C1" |
设计扩展的目的(Motivation)
比如在Java中,集合操作的swap 方法的API设计:// Java Collections.swap(list, i, j) |
比较流畅的方式应该是list. swap(i, j),本身该操作对应list;但是很多时候API已经设计好或者不能实现所有的可能的方法;这时候可以通过扩展方式来灵活实现需要的功能。
相关文章推荐
- Kotlin语法学习-变量定义、函数扩展、Parcelable序列化、编写工具类、Activity跳转
- Kotlin语法学习-变量定义、函数扩展、Parcelable序列化等简单总结
- Kotlin语法学习-变量定义、函数扩展、Parcelable序列化、编写工具类、Activity跳转
- kotlin学习--基本语法
- Kotlin Android 扩展-替换findViewById()的超简洁插件
- Kotlin语法(八)-接口
- Kotlin 基本语法
- Kotlin基本语法
- Kotlin常用语法总结
- PHP扩展之文本处理(二)——PCRE正则表达式语法12——一次性子组
- Kotlin 基础语法
- Kotlin学习(五): 惯用语法和代码风格(Idioms And Coding Conventions)
- kotlin官方文档中文翻译(一)基础语法,习惯用法,编码习惯
- ExtJs4 笔记(2) ExtJs对js基本语法扩展支持
- Kotlin 与 Java基本语法对比
- KnockoutJS 3.X API 第五章 高级应用(5) 使用预处理扩展Knockout绑定语法
- Kotlin系列一(基本语法)
- Linux扩展语法
- Kotlin语法(五)
- [Kotlin]Kotlin学习笔记(一):环境搭建及Kotlin特色语法学习笔记