Kotlin 修煉手冊(4)類與對象

前言

在面向對象編程中,類與對象是很是重要的概念,這篇文章來寫一下 Kotlin 中的類與對象。html

類的定義與對象聲明

Kotlin 中使用 class 關鍵字來定義類。java

class Person{}

//若是是空類,能夠這樣寫:
class Person
複製代碼

在聲明一個對象時,對比 Java,Kotlin 中省略了 new 這個關鍵字。編程

val p = Person()
複製代碼

類的屬性和函數

類的屬性能夠用 var 或 val 來聲明,var 可變,val 不可變。要使用一個屬性,直接用名稱來引用它。後端

class Person{
	var name: String = ""
	var age: Int = 0

	fun say(){
		println("hello")
	}
}
 
fun main(){
	val p = Person()
	p.name = "tom"
	println(p.name + p.age) //使用.來引用
	p.say()  
}
複製代碼

Kotlin 中函數的參數不能用 var 和 val 來修飾,但默認是 val 的,保證了參數不可被修改。bash

getter 和 setteride

Java 中通常將變量定義爲 private,搭配 public 的 getter 和 setter 方法,好比這樣:函數

class Person{
	private String name;
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
}
複製代碼

Kotlin 中默認爲咱們實現了 getter 和 setter 方法,val 聲明的變量只有 getter,沒有 setter,由於它是隻讀的。ui

能夠自定義屬性的 getter 和 setter 方法,進行一些額外的處理:this

class Person {
    var name: String = ""
        get() {
            return field.toUpperCase()
        }
        set  // 直接寫set或者省略都使用默認的

    var age: Int = 0
        get() = field   //這裏也能夠寫get、或者省略不謝
        set(value) {   //value的類型必定是和屬性一致,因此這裏能夠省略類型
            if (value < 0) {
                field = 0
            } else {
                field = value
            }
        }
}

fun main() {
    val p = Person()
    p.name = "tom"
    p.age = -1
    println("name = ${p.name}")
    println("age = ${p.age}")
}
複製代碼

上面例子中有一個關鍵字 field,這是Kotlin中的後端變量(Backing Fields),能夠簡單理解爲當前這個屬性的備用名,用於在 getter 和 setter 方法中指代該屬性。spa

爲何不能用屬性的名稱而要用一個備用名呢?這是由於在 Kotlin 中,一遇到屬性賦值就會轉化成 setter 函數,一遇到屬性的引用就會轉化成 getter 函數,從而造成遞歸,最終由於內存溢出而形成程序崩潰。

詳細能夠查看這篇文章的評論部分,有很是詳盡的解釋:Kotlin 類和對象

構造器

咱們先來看看 Java 中的構造函數。

public class ClassA {
    private String name;
    private int age;

    public ClassA(){
        this("",0);
    }

    public ClassA(String name){
        this(name,0);
    }

    public ClassA(String name, int age){
        this.name = name;
        this.age = age;
    }
}
複製代碼

能夠看到,Java 中的構造函數是一種特殊的成員函數,沒有返回值,通常爲 public,在不寫構造函數的時候編譯器會自動生成一個無參空實現的構造函數,能夠重載。

在 Kotlin 中,有主構造器和次級構造器之分,使用 constructor 關鍵字表示。不用 public 修飾符,默認是公開的。

主構造器

主構造器寫在類的聲明中,若是主構造器沒有任何註解和修飾符,能夠不加 constructor 關鍵字。主構造器中的參數能夠在 init 代碼塊中被訪問。還能夠加上 var 或 val 來直接將參數聲明成類的屬性。

class Person constructor(name: String) {  //constructor可省略
    val name: String
    init {
        this.name = name
        println("constructor")
    }
}
fun main() {
    val p = Person("tt")
    println("name = ${p.name}")
}
//運行結果
name = tt
複製代碼

在 init 代碼塊中,能夠進行屬性的賦值和執行初始化代碼。

也能夠寫成下面這樣,意思是同樣的。

class Person(val name: String) {}
fun main() {
    val p = Person("tt")
    println("name = ${p.name}")
}
//運行結果
name = tt
複製代碼

能夠看到和 Java 的對比,Kotlin 是將一個構造函數拆開了,參數部分寫在類頭,函數體部分寫在了 init 代碼塊中。

次級構造器

主構造器和次級構造器之間並無什麼本質區別,只是主構造器中的參數列表是最經常使用的。

次級構造器必須加上 constructor 關鍵字,並且必須使用 this 關鍵字顯式調用主構造器。看下面這個例子:

class Person constructor(name: String) {
    val name: String
    init {
        this.name = name
        println("主構造器調用")
    }

    constructor():this("owen"){
        println("次級構造器調用")
    }
}
fun main() {
    val p = Person()
    println("name = ${p.name}")
}
//運行結果
主構造器調用
次級構造器調用
name = owen
複製代碼

若是沒有調用主構造器,編譯器會直接報錯:

img1

其餘類

抽象類

抽象類的概念和 Java 中一致。若是一個類中有一個或多個抽象方法,那這個類就要被定義成抽象的,使用 abstract 關鍵字進行修飾。

abstract class A{
    abstract fun foo()

    fun foo2(){
        println("hello")
    }
}
複製代碼

嵌套類

能夠把一個類定義在另外一個類中,一般這兩個類之間有必定業務上的聯繫。

class Outter{
    val age = 0
    class Nested{
        val name = "tom"
    }
}

fun main() {
    val n = Outter.Nested()
    println(n.name)
}
複製代碼

內部類

Kotlin 中內部類使用 inner 關鍵字表示,內部類持有一個外部類對象,因此能夠訪問外部類的屬性和函數(即便是 private 的)。

class Outter{
    val age = 0
    inner class Inner{
        val name = "tom"

        fun say(){
            println("外部類age=$age")
        }
    }
}

fun main() {
    val n = Outter().Inner()
    println(n.name)
    n.say()
}
複製代碼

總結一下嵌套類和內部類的幾點區別:

  1. 嵌套類沒有關鍵字修飾,內部類使用 inner關鍵字
  2. 嵌套類的聲明方式是 Outter.Nested(),內部類是 Outter().Inner(),嵌套類中外部類後面沒有括號,由於沒有實例化外部類的對象。
  3. 嵌套類不能訪問外部類的屬性和函數,內部類能夠。

匿名內部類

使用對象表達式來建立匿名內部類,必須要使用 object 關鍵字做爲假設的變量名。

以 Android 中 Button 的點擊事件爲例:

class Button{
    var text = "文字"
    fun setOnClickListener(listener: OnClickListener){
        listener.onClick()
    }
}

/**
 * 定義接口
 */
interface OnClickListener {
    fun onClick()
}

fun main(){
	var button = Button()
    button.setOnClickListener(object : OnClickListener{
        override fun onClick() {
            println("點擊了button")
        }
    })
}
複製代碼

修飾符

Kotlin 中類的修飾符分爲兩種,一種是類屬性修飾符(classModifier),用於標識類自己屬性,一種是訪問權限修飾符(accessModifier)。

類屬性修飾符有:

abstruct    -> 抽象類(或者修飾抽象方法)
final       -> 類不可被繼承,默認爲final
enum        -> 枚舉類
open        -> 能夠被繼承
annotation  -> 註解類
複製代碼

權限訪問修飾符有:

private    -> 在同一個文件中可見
protected  -> 同一個文件中或子類可見
public     -> 全部調用的地方均可見
internal   -> 同一個模塊中可見
複製代碼

靜態變量和方法

在 Java 中,可使用 static 關鍵字定義類層面的變量和方法;在 Kotlin 中,使用伴生對象來實現類的靜態屬性和函數。這個在以後進行介紹。

結語

下一篇是關於繼承和接口的內容。

參考資料

Kotlin 類和對象

Kotlin 裏那些「不是那麼寫的」

相關文章
相關標籤/搜索