當Android遇上Kotlin — Day3

類別與物件

繼承類別

  • open修飾符是用來表示該類別可以被繼承,原本Java是使用public來做為預設值,但在Kotlin是使用final來做為類別的預設值,所以當該類別需要被其他類別所繼承時,必須先在父類別設定為open才能使用
// 其他類別可以繼承這個類別
open class RichButton : Clickable {
// 這個函數是不可以被修改的(final),不可以在子類別中override它
fun disable() {}
    // 這個函數是可以被修改的(open),可以在子類別中override它
open fun animate() {}
    // 這函數override了一個已經open的函數,所以他也是可以被override的
override fun click() {}
    // 但是如果你不希望子類別去override其父類別所繼承的函數時,可使用final 
final override fun click() {}
}

抽象類別

  • 由於抽象類別是不需被初始化,內部也會包含許多需要在子類別去實作抽象的成員函數,因此成員函數預設是open,不需特別定義
// 這是抽象類別
abstract class Animated {

// 這是抽象函數,不需再父類別進行實作,但必須在子類別去override並實作
abstract fun animate()
    // 抽象類別中的非抽象函數預設是final,但是可以透過open來打開它
open fun stopAnimating() { ... }

fun animateTwice() { ... }
}

類別的可見性修飾符

  • public(預設值):代表所有地方都能使用這類別
  • internal:代表只有在相同的module內才可以使用這類別
  • protected:代表只有在子類別中能使用
  • private:代表只有該類別內可以使用

內部類別,嵌套類別,密封類別

  • 預設為嵌套類別
  • 透過inner來表示該類別為內部類別
  • 透過sealed來將該類別進行密封,所有子類別都必須嵌套在父類別當中
// 嵌套類別
class Outer {
private val bar: Int = 1
class Nested {
fun foo() = 2
}
}

val demo = Outer.Nested().foo() // == 2
// 內部類別
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}

val demo = Outer().Inner().foo() // == 1
// 限定this表達式,用標籤限定符(@)label來表示this的來源
class Outer {
inner class Inner {
fun getOuterReference(): Outer = this@Outer
}
}

類別建構子

class User constructor(_nickname: String) { // 主建構子

val nickname: String

init { // 初始化
nickname = _nickname
}
}
// 其實就跟以下程式是相同的
class User(val nickname: String) { ... }

使用data類別修飾符,來建立數據類別

  • 一般在使用Java來建立一個數據Model時,必須去override掉toString(),hashCode(),equals()等function來方便日後的判斷與顯示,現在在Kotlin中,只需要在該類別宣告為data的修飾符,Kotlin會自動幫你生成
data class Client(val name: String, val postalCode: Int)

使用by來進行類別委託

  • 透過by來進行委託,編譯器會自動幫你生成,如此一來你只需要改變你所需要override的方法,而不需要實作所有方法,因為其他方法會透過委託的方式,執行編譯器所生成的實作
// 建立interface
interface Base {
fun print()
}

// 實現此interface的類別
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}

// 透過by來建立類別委託
class Device(b: Base) : Base by b

fun main(args: Array<String>) {
val b = BaseImpl(10)
Device(b).print() // 輸出:10
}

使用object讓Singleton模式更加容易

// Java
private static CaseInsensitiveFileComparator INSTANCE;
public static CaseInsensitiveFileComparator() {
if(INSTANCE == null) {
INSTANCE = new CaseInsensitiveFileComparator();
}
return INSTANCE;
}
@Override
public void compare(File file1, File file2) {
return file1.getPath().compareTo(file2.getPath(), true)
}
CaseInsensitiveFileComparator.INSTANCE.compare(
new File("/User"), File("/user"));
// Kotlin
object CaseInsensitiveFileComparator : Comparator<File> {
override fun compare(file1: File, file2: File): Int {
return file1.getPath().compareTo(file2.getPath(),
ignoreCase = true)
}
}
CaseInsensitiveFileComparator.compare(File("/User"), File("/user"))

另外使用sortedWith()函数,他會透過傳入一個具體的比較物件回傳列表

data class Person(val name: String) {
object NameComparator : Comparator<Person> {
override fun compare(p1: Person, p2: Person): Int =
p1.name.compareTo(p2.name)
}
}
val persons = listOf(Person("Bob"), Person("Alice"))
print(persons.sortedWith(Person.NameComparator))
[Person(name="Alice"), Person(name="Bob")]

使用companion來替代 static 的靜態方法

class A {
companion object {
fun bar() {
println("Companion object called")
}
}
}
A.bar() // 顯示:Companion object called
class User(val nickname: String) {
companion object { // 宣告companion
fun newSubscribingUser(email: String) =
User(email.substringBefore('@'))
       fun newFacebookUser(accountId: Int) =   
User(getFacebookName(accountId))
}
}
val subscribingUser = User.newSubscribingUser("bob@gmail.com")
val facebookUser = User.newFacebookUser(545566)

companion的擴展

interface JSONFactory<T> {
fun fromJSON(jsonText: String): T
}
class Person(val firstName: String, val lastName: String) {

companion object { // 宣告一個空的companion
}
}
fun Person.Companion.fromJSON(json: String): Person {
// 宣告一個擴展函數
...
}
val p = Person.fromJSON(json)
One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.