一、interfaces
普通的接口声明:
interface Clickable {
fun click()
}
实现该接口时,override修饰符不可省略,这点与Java不同。
class Button : Clickable {
override fun click() {
TODO("Not yet implemented")
}
}
接口中定义的方法可以有默认的实现:
interface Clickable {
fun click()
fun showOff() = println("I'm clickable!")
}
如果两个接口定义了同样的方法时
interface Focusable {
fun setFocus(b:Boolean) =
println("I ${if (b) "got" else "lost"} focus.")
fun showOff() = println("I'm focusable!")
}
编译器强制继承这两个接口的类必须实现这个相同的方法:
class Button : Clickable, Focusable {
override fun click() = println("I was clicked")
override fun showOff() {
super.showOff() //等同于Java的Clickable.super.showOff()
super.showOff() //等同于Java的Focusable.super.showOff()
}
}
二、access修饰符:classes和methods 默认是final的
Kotlin的classes和methods默认修饰符是final,这就意味着如果要允许子类存在,需要手动使用open修饰符。
//open class, 允许其它类继承.
open class RichButton : Clickable, Focusable {
fun disable() {} //方法默认为final,表示不可再子类中override.
open fun animate() {} //open方法,可以在子类中override.
override fun click() {} //overrides自一个open方法,因此也是open方法.
override fun showOff() {
super.showOff()
super.showOff()
}
//手动标识为final方法,表示不可在子类中override.
final override fun setFocus(b: Boolean) {
super.setFocus(b)
}
}
data class
data class 需要满足一下要求:
- primary constructor至少要有一个参数;
- 所有的primary constructor参数都需要被标记为val或者var;
- data class 不能是abstract, open, sealed, 或者inner的.
data class User(val name: String, val age: Int)
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"
data class的参数可以有默认值,从最后一个参数起,如果有默认值的话,那么实例化时可以不用传参。
data class User(val name: String = "", val age: Int = 20)
val defaultUser = User("Default")
abstract class Animated {
abstract fun animate() //abstract方法, 必须被子类override.
open fun stopAnimating() {} //open方法,可以被子类override.
fun animateTwice() {} //final方法, 不可以被子类override.
}
sealed class
sealed class是带继承限制的abstract class。继承该sealed class的类必须和该sealed class在同一文件内。
从而就限制了在外部文件中实现该sealed class的子类的可能,提高了安全性。
举例:
abstract class LoadState
data class Success(val dataFetched: String?): LoadState()
data class Error(val exception: Exception):LoadState()
object NotLoading: LoadState()
object Loading: LoadState()
fun getStateOutput(loadState:LoadState){
return when(loadState) {
is Error -> {
println(loadState.exception.toString())
}
is Success -> {
//If the dataFetched is null, return a default string.
println(loadState.dataFetched ?: "Ensure you startFetch first")
}
is Loading -> {
println("loading...")
}
is NotLoading -> {
println("IDLE")
}
//you have to add an else branch because the compiler cannot know whether the abstract class is exhausted.
else -> println("invalid")
}
}
如果换成sealed class来实现:
import java.io.IOException
sealed class LoadState
sealed class Error : LoadState() {
data class CustomIOException(val ioException: IOException) : Error()
data class CustomNPEException(val npeException: NullPointerException) : Error()
}
data class Success(val dataFetched: String?) : LoadState()
object NotLoading : LoadState()
object Loading : LoadState()
fun getStateOutput(loadState: LoadState) {
return when (loadState) {
is Success -> {
//If the dataFetched is null, return a default string.
println(loadState.dataFetched ?: "Ensure you startFetch first")
}
is Loading -> {
println("loading...")
}
is NotLoading -> {
println("IDLE")
}
is Error.CustomIOException -> {
println(loadState.ioException.toString())
}
is Error.CustomNPEException -> {
println(loadState.npeException.toString())
}
//这里不需要else,因为LoadState的所有继承类都在文件中枚举了,不可能出现其他的继承类。
else -> {
//'when' is exhaustive so 'else' is redundant here
}
}
}
三、visibility修饰符:默认public
注意点:
- a module usually consists of several packages, and different modules may contain declarations from the same package.
- an outer class doesn’t see private members of its inner (or nested) classes in Kotlin.
四、内部类(inner class)
Kotlin中内部类默认不持有外部类的引用(reference),相当于Java的static class,如果需要持有外部类的引用,需要手动添加inner关键字。
举例:
class Outer {
inner class Inner {
fun getReference(): Outer = this@Outer
}
}
五、Primary constructor和Secondary constructors
使用圆括号阔起constructor parameters,这个称作Primary constructor。
//“val” means the corresponding property is generated for the constructor parameter
class User(val nickname: String)
带有默认值的构造方法参数:
//Provides a default value for the constructor parameter
class User(val nickname: String, val isSubscribed: Boolean = true)
Secondary constructor的例子:
继承关系:
六、在接口中声明抽象属性(abstract property)
如下所示:
interface User {
val nickName: String
}
这意味着User接口的实现类需要提供获取nickName值的方式,interface本身没有规定是存储于backing field还是通过getter获取。
以下是几种使用方式:
class PrivateUser(override val nickName: String) : User //Primary constructor property
class SubscribingUser(val email: String) : User {
//Implement members
override val nickName: String //Custom getter
get() = email.substringBefore('@')
}
class FacebookUser(val accountId: Int) : User {
override val nickName = getFacebookName(accountId) //Property initializer
}
另外,interface还可以包含带有getters和setters的properties,只要这些properties不持有backing field。
interface User {
val email: String
val nickName: String //Property doesn’t have a backing field: the result value is computed on each access.
get() = nickName.substringBefore('@')
}
在setter中访问the backing field
class Account(val name: String) {
var address: String = "unspecified"
set(value: String) {
println(
"""Address was changed for $name:
"$field" -> "$value".""".trimIndent() //Reads the backing field value
)
field = value //Updates the backing field value
}
}
fun main() {
val account = Account("Tom")
println(account.address)
account.address = "ShangHai"
println(account.address)
account.address = "BeiJing"
println(account.address)
}
另外一个例子:
class Sword(_name: String) {
var name = _name
get() = "The legacy $field"
set(value) {
println("set value: $value, field: $field")
field = value.toLowerCase().reversed().capitalize()
}
init {
println("init : $_name")
name = _name
}
}
fun main(args: Array) {
val s = Sword("Chris")
println(s.name)
println("-----------")
s.name = "Haha"
println(s.name)
}
输出:
init : Chris
set value: Chris, field: Chris
The legacy Sirhc
-----------
set value: Haha, field: Sirhc
The legacy Ahah
修改set/get的可见性
class LengthCounter {
var counter: Int = 0
private set
fun addWord(word: String) {
counter += word.length
}
}
fun main() {
val lengthCounter = LengthCounter()
lengthCounter.addWord("Hello")
println(lengthCounter.counter)
lengthCounter.addWord("World")
println(lengthCounter.counter)
lengthCounter.counter += 10 //compile error: Cannot assign to 'counter': the setter is private in 'LengthCounter'
}
通用的object方法
- String representation: toString()
- Object equality: equals()
Kotlin中的"=="等价于equals()方法。 - Hash containers: hashCode()
class Client(val name: String, val postCard: Int) {
override fun equals(other: Any?): Boolean {
if (other == null || other !is Client) {
return false
}
return name == other.name && postCard == other.postCard
}
override fun toString(): String {
return "Client(name=$name, postCard = $postCard)"
}
override fun hashCode(): Int = name.hashCode() * 31 + postCard
}
fun main() {
val client1 = Client("Alice", 342562)
val client2 = Client("Alice", 342562)
println(client1 == client2) //true
val processed = hashSetOf(Client("Alice", 342562))
println(processed.contains(Client("Alice", 342562))) ///true
}
注意:
- The hashCode method should be always overridden together with equals.
- if two objects are equal, they must have the same hash code.
七、data class:自动生成通用的object方法
data class Client(val name: String, val postCard: Int)
fun main() {
val processed = hashSetOf(Client("Alice", 342562))
println(processed.contains(Client("Alice", 342562))) //true
}
注意:不在Primary constructor中声明的properties不参与equality的校验和hash code的计算。
定义data class需要遵循的原则:
- primary constructor至少要有一个参数;
- 所有的primary constructor参数都需要标记为var或val;
- data class不能是abstract, open, sealed, 或 inner;
- data class 不能继承自其它的classes,但是可以实现interfaces。
八 使用"by"关键字进行类委托(class delegation)
举例如下:
class DelegatingCollection : Collection {
private val innerList = arrayListOf()
override val size: Int get() = innerList.size
override fun contains(element: T): Boolean = innerList.contains(element)
override fun containsAll(elements: Collection): Boolean =
innerList.containsAll(elements)
override fun isEmpty(): Boolean = innerList.isEmpty()
override fun iterator(): Iterator = innerList.iterator()
}
其中DelegatingCollection类通过持有innerList实例,这就是常见的Decorator模式。
可以简写为:
class DelegatingCollection(innerList: Collection = ArrayList()) :
Collection by innerList {}
如果想改变某些方法的默认实现,可以重写这些方法。
class CountingSet(val innerSet: MutableCollection = HashSet())
:MutableCollection by innerSet {
var objectsAdded = 0
override fun add(element: T): Boolean {
objectsAdded++
return innerSet.add(element)
}
override fun addAll(elements: Collection): Boolean {
objectsAdded += elements.size
return innerSet.addAll(elements)
}
}
九、object关键字
object关键共出现在kotlin的三种场景中,他们所共有的核心思想是:
定义一个class、同时创建这个class的一个实例。
- object declaration是定义单例的一种方式;
- companion objects用来包含类的静态方法;
- object expression用来替换Java的匿名内部类(anonymous inner class)。The “object” keyword: declaring a class and creating an instance, combined。
object declaration
先看第一种:
object PayRoll {
val allEmployees = arrayListOf()
fun calculateSalary() {
for(person in allEmployees) {
...
}
}
}
object declaration将class的声明与该class的单一实例的声明组合在一起。object declaration可以声明properties、methods、initializer blocks等等,但是不允许有constructors。
object declaration在定义的时候就会创建类的实例,而不是通过调用构造方法来实现,这一点与常规的classes不同。
举例:
object CaseInsensitiveFileComparator : Comparator {
override fun compare(file1: File, file2: File): Int {
return file1.path.compareTo(
file2.path,
ignoreCase = true
)
}
}
fun main() {
println(CaseInsensitiveFileComparator
.compare(File("/User"), File("/user"))) //0
val files = listOf(File("/Z"), File("/a"))
println(files.sortedWith(CaseInsensitiveFileComparator)) //[/a, /Z]
}
在Java中使用Kotlin的单例的方法:
/* Java */
CaseInsensitiveFileComparator.INSTANCE.compare(file1, file2);
companion objects
companion object可以访问外部类的private成员,包括private constructor,因此这是实现Factory模式的较为理想的方式。
举例:
class User {
val nickName:String
constructor(email:String) {
nickName = email.substringBefore('@')
}
constructor(facebookAccountId:Int) {
nickName = getFacebookName(facebookAccountId)
}
}
改为使用factory方法来创建类的实例:
class User2 private constructor(val nickName: String) {
companion object {
fun newSubscriberUser(email:String) =
User(email.substringBefore('@'))
fun newFacebookUser(accountId:Int) =
User(getFacebookName(accountId))
}
}
fun main() {
val subscribingUser = User2.newSubscriberUser("[email protected]")
val facebookUser = User2.newFacebookUser(4)
}
在companion object中实现接口
为一个companion object定义扩展函数、
// business logic module
class Person(val firstName: String, val lastName: String) {
companion object {
}
}
// client/server communication module
fun Person.Companion.fromJSON(json: String): Person {
}
object expressions: 匿名内部类的另一种表述
object expression声明一个class、并且创建一个instance,但是不会为这个class或instance分配名字。通常来说,这个object是用作函数调用的的一个参数。
与Java不同的是,Kotlin的anonymous object可以实现多个接口。
anonymous objects不是单例的,每执行一次object expression,就会创建一个实例。
object expressions大多用在需要在匿名类中重写多个方法的时候。
object关键字还可以用来声明匿名内部类, 该匿名内部类不像java只能实现一个接口或继承一个对象,它可以实现多个接口。
nterface SchoolWork {
fun getCore()
}
interface HomeWork {
fun getProgress()
}
fun main(args: Array) {
doingHomeWork(object : HomeWork, SchoolWork {
override fun getCore() {
}
override fun getProgress() {
}
})
doingSchoolWork(object : HomeWork, SchoolWork{
override fun getCore() {
}
override fun getProgress() {
}
})
}
同时,也可以定义一个匿名类对象的变量:
interface TaskStatusListener {
fun onFailure();
fun onSuccess();
}
val taskStatus = object : TaskStatusListener {
override fun onSuccess() {
println("onSuccess called")
}
override fun onFailure() {
println("onFailure called")
}
}
它还可以访问外部函数创建的变量,并且不用标识为final类型:
interface TaskStatusListener {
fun onFailure();
fun onSuccess();
}
fun getTaskStatus(status: TaskStatusListener) {
status.onFailure();
status.onSuccess();
}
fun main(args: Array) {
var count = 0;
val taskStatus = object : TaskStatusListener {
override fun onSuccess() {
count++;
println("onSuccess called")
}
override fun onFailure() {
count++
println("onFailure called")
}
}
getTaskStatus(taskStatus)
getTaskStatus(object : TaskStatusListener {
override fun onSuccess() {
count++;
println("onSuccess called")
}
override fun onFailure() {
count++
println("onFailure called")
}
})
println("count: $count")
}