//接口定义interface Clickable {fun click()}//实现接口class Button:Clickable{override fun click() = println("I was clicked")}
接口的方法可以有一个默认实现。
interface Clickable {fun click()fun showOff() = println("I'm clickable!") //带默认实现的方法}
假设存在同样定义了一个showOff方法并且有如下实现的另一个接口。
interface Focusable {fun setFocus(b: Boolean) =println("I ${if (b) "got" else "lost"} focus.")fun showOff() = println("I'm focusable!")}
类实现这两个接口,如果没有显式实现showOff,将会得到如下编译错误:
class Button : Clickable,Focusable {override fun click() = println("I was clicked")override fun showOff() {super<Clickable>.showOff()super<Focusable>.showOff()}}
Java的类和方法默认是open的,而kotlin中默认都是final的。如果你想允许创建一个类的子类, 需要使用open修饰符来标示这个类。此外,需要给每一个可以被重写的属性或方法添加open修饰符。
open class RichButton:Clickable{fun disable(){} //final函数open fun animate(){} //open函数override fun click() {} //这个函数重写了一个open函数它本身同样是open函数}
如果重写了一个基类或者接口的成员,重写了的成员同样默认是open的,如果你想改变这一行为,阻止你的子类重写你的实现,可以显式地将重写的成员标注为final。
open class RichButton:Clickable{final override fun click() {}}
抽象成员始终是open的,所以不需要显式地使用open修饰符。
abstract class Animated{abstract fun animate()//抽象类中的非抽象函数并不是默认open的,但是可以标注为open的open fun stopAnimating(){}fun animateTwice(){}}
与Java不同,Kotlin的嵌套类不能访问外部类的实例。
interface Exprclass Num(val value: Int) : Exprclass Sum(val left: Expr, val right: Expr) : Exprfun eval(e: Expr): Int =when (e) {is Num -> e.valueis Sum -> eval(e.right) + eval(e.left)else -> //必须检查else分支throw IllegalArgumentException("Unknown expression")}
总是不得不添加一个默认分支很不方便。更重要的是,如果你添加一个新的子类,编译器并不能发现有地方改变了,如果你忘记了添加一个新分支,就会选择默认的选项,这可能导致潜在的bug。
Kotlin为这个问题提供了一个解决方案:sealed类。为父类添加一个sealed修饰符,对可能创建的子类做出严格的限制。所有的直接子类必须嵌套在父类中。
sealed class Expr {class Num(val value: Int) : Expr()class Sum(val left: Expr, val right: Expr) : Expr()}fun eval(e: Expr): Int =when (e) { //不需要提供默认分支is Expr.Num -> e.valueis Expr.Sum -> eval(e.right) + eval(e.left)}
sealed修饰符隐含的这个类是一个open类,不再需要显式地添加open修饰符。
class User(val nickname: String)
这段被括号围起来的语句块就叫作主构造方法,它主要有两个目的:表明构造方法的参数,以及定义使用这些参数初始化的属性。
class User constructor(_nickname: String) {val nickname :Stringinit {nickname = _nickname}}
constructor
关键字用来开始一个主构造方法或从构造方法的声明。init
关键字用来引入一个初始化语句块。因为主构造方法有语法限制,不能包含初始化代码,所以要使用初始化语句块。一个类中可以声明多个初始化语句块。
构造方法参数_nickname
中的下划线用来区分属性的名字和构造方法参数的名字。另一个可选方案是使用同样的名字,通过this来消除歧义。
class User constructor(nickname: String) {val nickname :Stringinit {this.nickname = nickname}}
在这个例子中,不需要把初始化代码块放在初始化语句块中,因为它可以与nickname属性的声明结合。如果主构造方法没有注解或可见性修饰符,同样可以去掉constructor关键字。
class User(_nickname: String) {val nickname = _nickname}
class User(val nickname: String,val isSubscribed:Boolean = true)
open class User(val nickname: String)class TwitterUser(nickname: String) : User(nickname)open class Button //将生成一个不带任何参数的默认构造方法class RadioButton : Button() //注意与接口的区别,接口不带括号
如果不想类被其他代码实例化,必须把构造方法标记为private
。
class Secretive private constructor() {}