1.接口
kotlin的接口比较像Java8,可以有实现的方法
interface MyInterface {
fun bar()
fun foo() {
// optional body
}
}
接口中的属性可以是抽象的,也可以是提供实现的。
interface MyInterface {
val prop: Int // abstract
val propertyWithImplementation: String //实现的属性
get() = "foo"
fun foo() {
print(prop)
}
}
另外需要注意如果一个类同时实现两个接口,恰好这两个接口都有同一个同样的方法,那么这个类,必须有自己的实现方式,这是Kotlin为了避免冲突的解决方法。
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super<B>.bar()
}
}
2.可见作用域修饰符
kotlin有四种可见作用域修饰符:public,private,internal,protected,其中public是默认的,这和java的package可见作用域是默认的不一样。
- private 是 class类内可见
- public 是任何地方可见
- protected是private+子类可见
- internal 是 module可以见,这个module包括:intellij module, maven/gradle project, 一个ant task的文件集
3. 扩展
扩展就像装饰者模式,不需要继承,给类增加新功能。
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
一般写法是类名<泛型>.方法,所以你可以理解为static调用的方式。
open class C
class D: C()
fun C.foo() = "c"
fun D.foo() = "d"
fun printFoo(c: C) {
println(c.foo())
}
printFoo(D())
上面的结果是打印c,因为扩展,它依赖于函数定义时传递的类型(如上是C),而不是运行时传递的类型(如上是D)
class C {
fun foo() { println("member") }
}
fun C.foo() { println("extension") }
如果执行c.foo(),那么结果会是member,就是说如果扩展了同样的方法(包括返回类型,方法名,参数名),那么实际上这个扩展是没有意义的。
但是如果增加了一个覆载的方法就不一养
class C {
fun foo() { println("member") }
}
fun C.foo(i: Int) { println("extension") }
如果执行c.foo(1), 结果会打印extension,因为扩展了C类中没有的方法,因而这个方法是有效地。
而且还有扩展属性
val <T> List<T>.lastIndex: Int
get() = size - 1
但是需要注意属性扩展只能使用set/get,而不能直接初始化,比如下面代码是错误的
val Foo.bar = 1 // error: initializers are not allowed for extension properties
一般情况下扩展会应用到top-level
package foo.bar
fun Baz.goo() { ... }
也可以定义到类里作为成员函数
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@C)
class C {
fun D.foo() {
toString() // calls D.toString()
this@C.toString() // calls C.toString()
}
扩展的目的是为了简化代码如下:
// Java
Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))
//kotlin扩展简化后,代码可阅读性也提高了
// Java
list.swap(list.binarySearch(otherList.max()), list.max())
3.数据类
有时候定义一个类仅仅是为了使用它的数据,那么可以定义数据类
data class User(val name: String, val age: Int) // 使用data关键字
然后编译器会帮我们创建好equals/hashcode, toString(),copy方法
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
4.密封类
密封类是为了维护严格的类层次关系。使用sealed关键字修饰,密封类可以有子类,但是它和它的子类都必须在一个kotlin文件中声明.
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}