kotlin中的類
class Bird{
val weight:Double = 500.0
val color:String = "blue"
val age:Int = 1
fun fly(){}
}
上面代碼反編譯成Java的版本后欢瞪,會有g(shù)et方法淌实,除此之外還有一些不同
1)不可變屬性成員沃斤。這是利用java中的final修飾符來實現(xiàn)的鬼譬,使用var聲明屬性則引用可變陨瘩。
2)屬性默認(rèn)值腕够。Java中的屬性有默認(rèn)值,kotlin中舌劳,除非顯試的聲明延遲初始化帚湘,不然就需要指定屬性的默認(rèn)值。
3)不同的可訪問修飾符甚淡。kotlin中默認(rèn)是public大诸,而Java中默認(rèn)是 default
可帶有屬性和默認(rèn)方法的接口
Kotlin中的接口
interface Flyer{
val speed:Int
fun kind()
fun fly(){println("I can fly")}
}
kotlin中的接口是不能像java那樣直接賦值的 不過可以通過get方法來實現(xiàn)
interface Flyer{
val speed:Int
get() = 100
}
如果 接口中沒有初始化,則需要在子類中重寫進行初始化
class parot() :Bird {
override val speed: Int
get() =101
}
有了類就需要構(gòu)造方法
kotlin中可以通過構(gòu)造方法默認(rèn)參數(shù) 實現(xiàn)構(gòu)造方法重載
舉個??
class Bird(val weight:Double = 0.00,val age:Int = 1)
如果用Java來實現(xiàn)同樣的效果 需要幾個構(gòu)造方法贯卦? 4個资柔。
構(gòu)造方法中可以用val 和var來聲明構(gòu)造方法的參數(shù),val代表引用不可變 撵割,那么在構(gòu)造方法中用val修飾這個變量還能被賦值嗎贿堰,當(dāng)然可以。聲明后代表在類的內(nèi)部聲明了一個同名的屬性,類似一下實現(xiàn)
class Bird(weight:Double = 0.0,age:Int= 0){
val weight:Double
val age: Int
init{
this.weight = weight
this.age = age
}
}
上面的代碼很有意思啡彬,說明kotlin也可以分開初始化官边,還有用val 修飾的變量是在初始化之后不可變沸手。
init語句塊 是構(gòu)造方法的一部分,兩者在表現(xiàn)形式上是分離的注簿,如果在初始化時進行其他的額外操作契吉,那么我們就可以使用init語句塊來執(zhí)行 。
構(gòu)造方法的參數(shù)可以在init語句中訪問诡渴,也可以用于初始化類內(nèi)部的屬性成員的情況
class Bird(weight:Double = 0.00){
val weight:Double = weight
}
但是不能在成員函數(shù)中訪問
class Bird(weight:Double){
fun printWeight(){
print(weight) // 這里不能直接訪問 weight
}
}
主從構(gòu)造方法
我們可能需要從一個特殊的數(shù)據(jù)來獲取構(gòu)造類的參數(shù)值捐晶,這時候如果可以定義一個額外的構(gòu)造方法,接收一個自定義的參數(shù)會顯的特別方便
class Bird(val age:Int){
constructor(birth:DateTime) :this(getAgeByBirth(birth)){}
}
類外部定義的構(gòu)造方法被稱為主構(gòu)造方法妄辩,每個類可最多存在一個主構(gòu)造方法惑灵,和多個從構(gòu)造方法,如果主構(gòu)造方法存在注解或者可見星修飾符眼耀,則constructor 關(guān)鍵字不可省略英支。從構(gòu)造方法會先執(zhí)行對其他構(gòu)造方法的委托,然后執(zhí)行自身代碼塊的邏輯; 如果存在主構(gòu)造方法 那么每個從構(gòu)造方法都要直接或者間接的委托給它哮伟。
不同的訪問控制原則
繼承雖然靈活干花,但是如果被濫用會引起一些問題。舉個?? 我認(rèn)為企鵝也是一種鳥楞黄, 于是聲明了一個Penguin類來繼承Bird
open class Bird{
open fun fly(){ print("I can fly.")}
}
class Penguin :Bird(){
override fun fly(){
print("I can't fly actually")
}
}
kotlin中的繼承是用:池凄,kotlin中類和方法默認(rèn)是不可被繼承或重寫的,所以必須加上open 修飾符鬼廓。上面的例子明顯不太好肿仑,所以kotlin中默認(rèn)類和方法都是加了final修飾的
sealed
用sealed修飾的類 不能被初始化,因為他背后是基于一個抽象類實現(xiàn)的碎税,若要繼承則需要將子類定義在用一個文件中(同一個包也不行尤慰,就是同一個文件)
可見性修飾符
- kotlin與Java的默認(rèn)修飾符不同,kotiin中是public 雷蹂,而Java中是default
2)kotlin中有個獨特的修飾符internal (模塊內(nèi)可見)
3)kotlin可以在一個文件內(nèi)單獨聲明方法以及常量伟端,同樣支持可見性修飾
4)Java中除了內(nèi)部類可以用private修飾以外,其他類都不允許private修飾萎河,而kotlin可以
5)protected的訪問訪問范圍不同荔泳,Java中是包,類子類虐杯,而kotlin中只允許類及子類玛歌。
模塊內(nèi)訪問(internal)
- 一個Eclipse項目
- 一個Intellij IDEA項目
- 一個Maven項目
- 一個Grandle項目
- 一組由一次Ant任務(wù)執(zhí)行編譯的代碼
inner
kotlin中的內(nèi)部類默認(rèn)是static 的,所以想要定義一一個內(nèi)部類 要加上inner關(guān)鍵字
(如果不加則沒有外部類的引用擎椰,無法訪問外部類的屬性和方法)
通過委托來代替多繼承實現(xiàn)需求
interface CanFly{
fun fly()
}
interface CanEat{
fun eat()
}
class Flyer:CanFly {
override fun fly() {
println("i can fly")
}
}
class Animal: CanEat{
override fun eat() {
println(" i can eat")
}
}
class Bird(flyer:Flyer,animal: Animal) :CanFly by flyer ,CanEat by animal
object Test{
@JvmStatic
fun main(args: Array<String>) {
val flyer = Flyer()
val animal = Animal()
val bird = Bird(flyer,animal)
bird.eat()
bird.fly()
}
}
疑問:首先支子,委托方式怎么跟接口實現(xiàn)多繼承如此相似,而且好像也沒有簡單多少达舒,其次值朋,這種方式好像跟組合也很像叹侄,那么它到底有什么優(yōu)勢呢?只要有以下亮點:
1)接口是無狀態(tài)的昨登,所以即使它提供了默認(rèn)方法實現(xiàn)也是很簡單的趾代,不能實現(xiàn)復(fù)雜邏輯(也不推薦) 我們可以利用上面委托的這種方式,雖然他也是接口委托丰辣,但是是用一個具體的類去實現(xiàn)方法邏輯撒强,可以擁有更強大的邏輯
2)假設(shè)我們需要繼承的類是A,委托對象是B笙什,C飘哨,我們在具體調(diào)用的時候并不是像組合一樣A.B.method,而是可以直接調(diào)用A.medthod,這樣更能表達A擁有該mehod的能力琐凭,更加直觀
真正的數(shù)據(jù)類
有這樣一種場景芽隆,我們并不想要那么強大的類,也許我們只是想要單純的使用類來封裝數(shù)據(jù)统屈,類似于Java中的DTO(Data transfer object)的概念胚吁,但是我們知道在Java中顯的繁瑣,因為通常情況下我們會聲明一個javaBean鸿吆,然后定義一堆getter和setter囤采。但是你很可能已經(jīng)厭煩了這些冗長的代碼述呐,那來看看kotlin是如何改進這個問題的吧
用data class 創(chuàng)建數(shù)據(jù)類
data class 顧名思義就是數(shù)據(jù)類
data class Bird(var weight:Double ,var age:Int,var color:String)
那么 這個data關(guān)鍵字幫我們做了哪些事情
1)提供get set方法
2)重寫了toString 方法惩淳,重寫equals 和hashcode
3)提供了一些方法 如 copy 和componentN
val b1 = Bird(3.0,1)
val b2 = b1.copy(age = 2)
copy 內(nèi)部實現(xiàn)是通過傳過來的參數(shù)重新new了一個對象
接下來我們看看componentN方法。簡單來說乓搬,componentN可以理解為類屬性的值思犁,其中N代表屬性的順序,比如component1代表第一個屬性的值进肯,那么這樣設(shè)計到底有什么用呢激蹲? 我們來思考一個問題,我們或多或少的知道怎么將屬性綁定到類上江掩,到那時如何將類的屬性綁定到相應(yīng)變量上卻不是很熟悉 如:
// 普通方式
val b1 = Bird(3.0,1,Color.BLACK)
val weight = b1.weight
val age = b1.age
val color = b1.color
//kotlin 進階
val (weight,age,color) = b1
只有提供了component函數(shù)的時候才可以這么寫
看到kotlin的語法信息你一定會感到興奮学辱,因為你可能寫過類似下面的代碼
String birdInfo = "20.0,1,bule";
String[] temps = birdInfo.split(".")
double weight = Double.valueOf(temps[0]);
int age = Integer.valueOf(temps[1]);
String color = temps[2];
這樣的代碼有時真的很繁瑣,我們明明知道值的情況环形,卻要分好幾步來給變量賦值策泣,很幸運,kotlin提供了更優(yōu)雅的做法:
val (weight,age,color) = birdInfo.split(".")
這里的原理就是解構(gòu)抬吟,通過編譯器的約定實現(xiàn)解構(gòu)萨咕,kotlin對數(shù)組的結(jié)構(gòu)有一定限制,在數(shù)組中它默認(rèn)最多允許賦值5個變量
自己實現(xiàn)對應(yīng)屬性的componentN方法火本,如:
//
data class Bird(val weight:Double,val age:Int,val color: Color){
var sex = 1
operator fun component4() = sex
constructor(weight: Double,age: Int, color: Color,sex:Int):this(weight, age, color){
this.sex = sex
}
}
//使用
val b1 = Bird(3.0,1,Color.BLACK危队,1)
val (weight,age,color,sex) = b1
除了數(shù)組支持解構(gòu)外聪建,kotlin也提供了其他常用的數(shù)據(jù)類,分別是Pair和Triple,使用如下
val(weightT,ageT,colorT) = Triple(20.0,1,Color.BLUE)
伴生對象
半生對象的設(shè)計目的就是為了把普通變量和靜態(tài)變量區(qū)別開茫陆,而不是混在一個類中靠static關(guān)鍵字區(qū)別金麸。
關(guān)于object關(guān)鍵字
兩個作用
1.天生的單例(餓漢式,線程安全)
2.寫匿名內(nèi)部類