Kotlin三 類和對象

一 Kotlin 類和對象

 

Kotlin 類能夠包含:構造函數和初始化代碼塊、函數、屬性、內部類、對象聲明。java

Kotlin 中使用關鍵字 class 聲明類,後面緊跟類名。編程

類的屬性能夠用關鍵字 var 聲明爲可變的,不然使用只讀關鍵字 val 聲明爲不可變。swift

 

咱們能夠像使用普通函數那樣使用構造函數建立類實例:後端

val site = Runoob() // Kotlin 中沒有 new 關鍵字

 Kotlin 中類不能有字段。提供了 Backing Fields(後端變量) 機制,備用字段使用field關鍵字聲明,field 關鍵詞只能用於屬性的訪問器ide

class ClassA {

    var name: String = "zhangsan"
        get() = field.toUpperCase()
        set

    var age: Int = 0
        get() = field
        set(value) {
            if (value < 10) {
                field = value
            } else {
                field = -1
            }
        }
    var city: String = "jx"

    fun foo() {
        print("foo")
    }


}

  

fun main(args:Array<String>)
{
    val site = ClassA();
    site.foo()

    site.name = "lisi"
    println("name = ${site.name}")//LISI

    site.age = 30
    println("age = ${site.age}") //-1

}

  

非空屬性必須在定義的時候初始化,kotlin提供了一種能夠延遲初始化的方案,使用 lateinit 關鍵字描述屬性:函數

public class MyTest {
    lateinit var subject: TestSubject

    @SetUp fun setup() {
        subject = TestSubject()
    }

    @Test fun test() {
        subject.method()  // dereference directly
    }
}

主構造器

主構造器中不能包含任何代碼,初始化代碼能夠放在初始化代碼段中,初始化代碼段使用 init 關鍵字做爲前綴。測試

 

class Person constructor(firstName: String) {
    init {
        println("FirstName is $firstName")
    }
}

  

 注意:主構造器的參數能夠在初始化代碼段中使用,也能夠在類主體n定義的屬性初始化代碼中使用。 一種簡潔語法,能夠經過主構造器來定義屬性並初始化屬性值(能夠是var或val):網站

class People(val firstName: String, val lastName: String) {
    //...
}

  若是構造器有註解,或者有可見度修飾符,這時constructor關鍵字是必須的,註解和修飾符要放在它以前。this

 

次構造函數

類也能夠有二級構造函數,須要加前綴 constructor:url

class Person { 
    constructor(parent: Person) {
        parent.children.add(this) 
    }
}

 若是類有主構造函數,每一個次構造函數都要,或直接或間接經過另外一個次構造函數代理主構造函數。在同一個類中代理另外一個構造函數使用 this 關鍵字:

class ClassB(val name:String)
{
constructor(name:String,age:Int):this(name)

init {

}
}

  若是一個非抽象類沒有聲明構造函數(主構造函數或次構造函數),它會產生一個沒有參數的構造函數。構造函數是 public 。若是你不想你的類有公共的構造函數,你就得聲明一個空的主構造函數:

class DontCreateMe private constructor () {
}

注意:在 JVM 虛擬機中,若是主構造函數的全部參數都有默認值,編譯器會生成一個附加的無參的構造函數,這個構造函數會直接使用默認值。這使得 Kotlin 能夠更簡單的使用像 Jackson 或者 JPA 這樣使用無參構造函數來建立類實例的庫。  

實例

class Runoob  constructor(name: String) {  // 類名爲 Runoob
    // 大括號內是類體構成
    var url: String = "http://www.runoob.com"
    var country: String = "CN"
    var siteName = name

    init {
        println("初始化網站名: ${name}")
    }
    // 次構造函數
    constructor (name: String, alexa: Int) : this(name) {
        println("Alexa 排名 $alexa")
    }

    fun printTest() {
        println("我是類的函數")
    }
}

fun main(args: Array<String>) {
    val runoob =  Runoob("菜鳥教程", 10000)
    println(runoob.siteName)
    println(runoob.url)
    println(runoob.country)
    runoob.printTest()
}

 輸出結果:

初始化網站名: 菜鳥教程
Alexa 排名 10000
菜鳥教程
http://www.runoob.com
CN
我是類的函數

 

抽象類

抽象是面向對象編程的特徵之一,類自己,或類中的部分紅員,均可以聲明爲abstract的。抽象成員在類中不存在具體的實現。

注意:無需對抽象類或抽象成員標註open註解。

open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}

  

嵌套類

咱們能夠把類嵌套在其餘類中,看如下實例:

class Outer {                  // 外部類
    private val bar: Int = 1
    class Nested {             // 嵌套類
        fun foo() = 2
    }
}

fun main(args: Array<String>) {
    val demo = Outer.Nested().foo() // 調用格式:外部類.嵌套類.嵌套類方法/屬性
    println(demo)    // == 2
}

  

內部類

內部類使用 inner 關鍵字來表示。

內部類會帶有一個對外部類的對象的引用,因此內部類能夠訪問外部類成員屬性和成員函數。

class Outer {
    private val bar: Int = 1
    var v = "成員屬性"
    /**嵌套內部類**/
    inner class Inner {
        fun foo() = bar  // 訪問外部類成員
        fun innerTest() {
            var o = this@Outer //獲取外部類的成員變量
            println("內部類能夠引用外部類的成員,例如:" + o.v)
        }
    }
}

fun main(args: Array<String>) {
    val demo = Outer().Inner().foo()
    println(demo) //   1
    val demo2 = Outer().Inner().innerTest()   
    println(demo2)   // 內部類能夠引用外部類的成員,例如:成員屬性
}

爲了消除歧義,要訪問來自外部做用域的 this,咱們使用this@label,其中 @label 是一個 代指 this 來源的標籤。  

 

匿名內部類

使用對象表達式來建立匿名內部類:

class Test {
    var v = "成員屬性"

    fun setInterFace(test: TestInterFace) {
        test.test()
    }
}

/**
 * 定義接口
 */
interface TestInterFace {
    fun test()
}

fun main(args: Array<String>) {
    var test = Test()

    /**
     * 採用對象表達式來建立接口對象,即匿名內部類的實例。
     */
    test.setInterFace(object : TestInterFace {
        override fun test() {
            println("對象表達式建立匿名內部類的實例")
        }
    })
}

  

類的修飾符

類的修飾符包括 classModifier 和_accessModifier_:

  • classModifier: 類屬性修飾符,標示類自己特性。

abstract    // 抽象類  
final       // 類不可繼承,默認屬性
enum        // 枚舉類
open        // 類可繼承,類默認是final的
annotation  // 註解類

  accessModifier: 訪問權限修飾符 

private    // 僅在同一個文件中可見
protected  // 同一個文件中或子類可見
public     // 全部調用的地方均可見
internal   // 同一個模塊中可見

 

 

二 繼承

 Kotlin 中全部類都繼承該 Any 類,它是全部類的超類,對於沒有超類型聲明的類是默認超類: 

class Example // 從 Any 隱式繼承

  

 Any 默認提供了三個函數:

equals()

hashCode()

toString()

  

注意:Any 不是 java.lang.Object。

若是一個類要被繼承,能夠使用 open 關鍵字進行修飾。

 

open class Base(p: Int)           // 定義基類

class Derived(p: Int) : Base(p)

構造函數

子類有主構造函數

若是子類有主構造函數, 則基類必須在主構造函數中當即初始化

 

open class Person(var name : String, var age : Int){// 基類

}

class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {

}

// 測試
fun main(args: Array<String>) {
    val s =  Student("Runoob", 18, "S12346", 89)
    println("學生名: ${s.name}")
    println("年齡: ${s.age}")
    println("學生號: ${s.no}")
    println("成績: ${s.score}")
}

子類沒有主構造函數

若是子類沒有主構造函數,則必須在每個二級構造函數中用 super 關鍵字初始化基類,或者在代理另外一個構造函數。初始化基類時,能夠調用基類的不一樣構造方法。

 

/**用戶基類**/
open class Person(name:String){
    /**次級構造函數**/
    constructor(name:String,age:Int):this(name){
        //初始化
        println("-------基類次級構造函數---------")
    }
}

/**子類繼承 Person 類**/
class Student:Person{

    /**次級構造函數**/
    constructor(name:String,age:Int,no:String,score:Int):super(name,age){
        println("-------繼承類次級構造函數---------")
        println("學生名: ${name}")
        println("年齡: ${age}")
        println("學生號: ${no}")
        println("成績: ${score}")
    }
}

fun main(args: Array<String>) {
    var s =  Student("Runoob", 18, "S12345", 89)
}

重寫

在基類中,使用fun聲明函數時,此函數默認爲final修飾,不能被子類重寫。若是容許子類重寫該函數,那麼就要手動添加 open 修飾它, 子類重寫方法使用 override 關鍵詞:

 

/**用戶基類**/
open class Person{
    open fun study(){       // 容許子類重寫
        println("我畢業了")
    }
}

/**子類繼承 Person 類**/
class Student : Person() {

    override fun study(){    // 重寫方法
        println("我在讀大學")
    }
}

fun main(args: Array<String>) {
    val s =  Student()
    s.study();

}

  若是有多個相同的方法(繼承或者實現自其餘類,如A、B類),則必需要重寫該方法,使用super範型去選擇性地調用父類的實現。

open class A {
    open fun f () { print("A") }
    fun a() { print("a") }
}

interface B {
    fun f() { print("B") } //接口的成員變量默認是 open 的
    fun b() { print("b") }
}

class C() : A() , B{
    override fun f() {
        super<A>.f()//調用 A.f()
        super<B>.f()//調用 B.f()
    }
}

fun main(args: Array<String>) {
    val c =  C()
    c.f();

}

 C 繼承自 a() 或 b(), C 不只能夠從 A 或則 B 中繼承函數,並且 C 能夠繼承 A()、B() 中共有的函數。此時該函數在中只有一個實現,爲了消除歧義,該函數必須調用A()和B()中該函數的實現,並提供本身的實現。 

屬性重寫

屬性重寫使用 override 關鍵字,屬性必須具備兼容類型,每個聲明的屬性均可以經過初始化程序或者getter方法被重寫。

你能夠用一個var屬性重寫一個val屬性,可是反過來不行。由於val屬性自己定義了getter方法,重寫爲var屬性會在衍生類中額外聲明一個setter方法

你能夠在主構造函數中使用 override 關鍵字做爲屬性聲明的一部分:

 

interface Foo {
    val count: Int
}

class Bar1(override val count: Int) : Foo

class Bar2 : Foo {
    override var count: Int = 0
}
相關文章
相關標籤/搜索