1. 程式人生 > >kotlin(3):類和物件以及其他的概念屬性

kotlin(3):類和物件以及其他的概念屬性

類和物件

類宣告由類名,類頭(指定其型別引數,主建構函式等)和由大括號包圍的類體構成,類頭和類體都是可選的,如果一個類沒有類體,可以省略花括號.

 

1.主建構函式

kotlin中的一個類可以有一個主建構函式和多個次建構函式,主建構函式是類頭的一部分,跟在類名後.

class Demo constructor(data: String){}

如果主建構函式沒有任何註解或者可見性修飾符,可以省略constructor關鍵字

class Demo(data: String){}

主建構函式不能包含任何的程式碼,初始化的程式碼可以放到以init關鍵字作為字首的初始化塊中

class Demo(data: String){
init{
print(“init”)
}
}

主建構函式的引數可以在初始化塊中使用,它們也可以在類體內宣告的屬性初始化器中使用

class Demo(var data: String){}

與普通屬性一樣,主建構函式中宣告的屬性可以是可變的var或者是隻讀的val

如果建構函式有註解或者可見性修飾符,這個constructor關鍵字是必需的,並且這些修飾符必須在它的前面

 

2.次建構函式

類也可以宣告字首有constructor的次建構函式

class Demo {
var data:Int = 0//若是以這樣的形式宣告成員變數必須賦予初值
constructor(data:Int) {//可以有多個不同引數的次建構函式
}
}

如果一個非抽象類沒有宣告任何(主或次)建構函式,它會有一個生成的不帶任何引數的主建構函式,建構函式的可見性是public,可以宣告一個帶有非預設可見性的空的主建構函式

class Demo private constructor(){}

:jvm,如果主建構函式的所有的引數都有預設值,編譯器會生辰給一個額外的無參建構函式,它將使用預設值

class Demo(var data:Int = 1) {}
var demo = Demo()//demo的成員變數的data的值為1

3.建立類的例項

Kotlin沒有new,這點是與java最不同的地方

var demo = Demo()//可變的物件 var
val demo = Demo(1);//不可變的物件 val

4.類成員

一個類可以包含:建構函式,初始化塊,函式,屬性,巢狀類和內部類,物件宣告

5.繼承

kotlin中所有的類都有一個共同的超類Any(類似於java中的Object),這對於沒有超型別宣告的類是預設超類

不過Any中的成員很少,只有equals() hashCode() toString()

 

要宣告一個顯式的超型別,把型別放到類頭的冒號之後:

open class Base()//可以定義為open或者abstract中的一個
class Demo():Base(){}

如果該類有一個主建構函式,其基類必須用基類的主建構函式引數就地初始化

如果類沒有主建構函式,那麼每個次建構函式必須用super關鍵字初始化其基類,或委託給另一個建構函式做到這一點,注意:在這種情況下,不同的次建構函式可以呼叫基型別的不同的建構函式

class MyView : View {
constructor(ctx: Context) : super(ctx)
constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
}

類上的open標註與javafinal相反,它允許其他類從這個類繼承,預設情況下,kotlin中所有的類都是final

 

6.覆蓋方法

Kotlin需要顯式標註可覆蓋的成員和覆蓋後的成員

open class Base{
open fun v(){}
}
 
class Demo():Base(){
Override fun v(){}
}


Demo類中的v()函式上必須加上override標註,如果沒寫,編譯器會報錯.如果函式沒有標註open則子類中不允許定義相同簽名的函式,不論加不加override.在一個final類中,開放成員是禁止的,相反在標記為override的成員本事是開放的,也就是說,它可以在子類中覆蓋,如果想禁止再次覆蓋,可以使用final關鍵字修飾,這樣就禁止被再次覆蓋了

open class Demo():Base(){
Final override fun v(){}
}

7.覆蓋屬性

屬性覆蓋與方法覆蓋類似,在超類中宣告然後在派生類中重新宣告的屬性必須以override開頭,並且它們必須具有相容的型別,每個宣告的屬性可以由具有初始化器的屬性或者具有getter()方法的屬性覆蓋


open class Base {
    open var data:Int = 1  
        get() {return 2}
}

class Demo : Base(){
    override var data:Int = 3
            get() = 4
}


最後的Demodata的值為4

可以用一個var覆蓋基類的val,但反之不行.

 

8.覆蓋規則

kotlin,實現繼承由下述規則規定:如果一個類從它的直接超類繼承相同成員的多個實現,它必須覆蓋這個成員並提供其自己的實現(也許用繼承來的其中之一).為了表示採用從哪個超型別繼承的實現,我們使用由尖括號中超型別名限定的super,super<Base>

open class Base {
    open fun f(){}
}
 
interface Base2{
     fun f(){}
 
}
 
class Demo : Base(),Base2{
    override  fun f(){
        super<Base2>.f()
        super<Base>.f()
    }
}


 

 

9.抽象類

類和其中的某些成員可以宣告為abstract,抽象成員在本類中可以不用實現,需要注意的是,我們並不需要用open標註一個抽象類或者函式,,因為是廢話..

 

 

可以用一個抽象成員覆蓋基類的費抽象的開放成員

10.伴生物件

java不同,kotlin中類沒有靜態方法,在多數情況下,它建議簡單地使用包級函式

如果需要寫一個可以無需用一個類的例項來呼叫,但需要訪問類內部的函式,也就是對java中的static的需要.kotlin中可以寫成該類內物件宣告中的一員,中文意思就是:如果在類內宣告一個伴生物件,就可以像在java中呼叫static相同的語法來呼叫其成員,只使用類名作為限定符.

如下:

open class Demo {
    companion object {
        fun f(){
        }
    }
}


注意:每一個類中只能有一個companion object 此時的物件名稱是Companion

也可以自定義名字:

open class Demo {
    companion object MyCompanion{
        fun f(){
        }
    }
}


呼叫的時候:

 

Demo.f()
 Demo.MyCompanion.f()

 

11.單例模式(物件宣告)

kotlin中單例實現很簡單,直接貼程式碼:

object Single{
    fun f(){
        println("single")
    }
}


注意:物件宣告不能在區域性作用域中,但是它們可以巢狀到其他物件宣告或非內部類中

 

物件表示式和物件宣告的區別:

A.物件表示式是在使用它們的地方立即執行,建立物件

B.物件宣告是在第一次被訪問到時延遲初始化的,懶漢式的單例模式

C.伴生物件的初始化是在相應的類被載入時,java靜態初始化的寓意

 

12.屬性和欄位

宣告屬性:

Kotlin的類可以有屬性,屬性可以用var宣告為可變的,否則使用只讀關鍵字val

 

class Demo{
var data1:String = “”
var data2:Int = 0
val data3:Int = 1
}


要使用一個屬性,名稱引用即可

GettersSetters

宣告一個屬性的完整語法是

var <propertyName> [:<propertyType>] [=<property_initializer>]
   [<getter>]
   [<setter>]


初始器gettersetter是可選的,屬性型別如果可以從初始器(或者從getter返回值)中推斷出來,也可以省略

EG:

  var demo1:Int? //錯誤:需要顯示地初始化,隱含預設getter和setter
  var demo2 = 1 //型別Int,預設getter和setter

一個只讀屬性的語法和一個可變的屬性的語法有兩方面的不同:1.只讀屬性的用val開始代替var, 2.只讀屬性不允許setter

val demo:Int?//型別 Int, 預設getter,必須在建構函式中初始化
val demo2 = 1//型別Int 預設getter

自定義gettersetter的例子

var demo:String
   get() = this.toString()
   set(value) {
      This.demo = value
   }

改變一個訪問器的可見性或者對其註解,但是不需要改變預設的實現,可以定義訪問器而不定義其實現

var demo:String=”hello”
   private set//此setter是私有的並且有預設的實現
Var demo2:Any? = null
       @Inject set //用Inject註解此setter


幕後欄位:

 Kotlin中類不能有欄位,當使用自定義訪問器的時候,有時有一個幕後欄位(backing field)有時是必需的.為此kotlin提供一個自動幕後欄位,可通過使用field識別符號訪問


var counter = 0 //此初始化值直接寫到幕後欄位裡面
   set(value) {
If(value > 0) filed = value
}

Field識別符號只能用在屬性的訪問器內

如果屬性至少一個訪問器使用預設實現,或者自定義訪問器通過field引用幕後欄位,將會為該屬性生成一個幕後欄位.

例如下面的情況,就沒有幕後欄位:

val isEmpty : Boolean
get() = this.size == 0

編譯期常量

已知值的屬性可以使用const修飾符標記為編譯期常量,這些屬性需要滿足以下的要求:

A.位於頂層或者是object的一個成員

B.String或原生型別值初始化

C.沒有自定義getter

這些屬性可以用在註解中:

const val demo:String=”hello world”

惰性初始化屬性

一般的,屬性宣告為非空型別必須在建構函式中初始化,然而這經常不方便,例如:屬性可以通過依賴注入來初始化,或者在單元測試的setup方法中初始化,這種情況下,不可以在建構函式中提供一個非空初始器,這時候可以用lateinit修飾符標記該屬性

lateinit var demo:String


該修飾符只能用於在類體中(不是在主建構函式中)宣告的var屬性,並且僅當該屬性沒有自定義getter或者setter,該屬性必須是非空型別,並且不能是原生型別.

在初始化前訪問一個lateinit屬性會丟擲一個特定異常,該異常明確標識該屬性被訪問及它沒有初始化的事實

 

委託屬性

最常見的一類屬性就是簡單地從幕後欄位中讀取,另一方面,使用自定義gettersetter可以實現屬性的任何行為,介於兩者之間,屬性如何工作有一些常見的模式