修飾符
訪問修飾符
| 修飾符 | 相關(guān)成員 | 評(píng)注 |
|---|---|---|
| final | 不能被重寫 | 類中成員默認(rèn)使用 |
| open | 可被重寫 | 需要明確什么 |
| abstract | 必須被重寫 | 在抽象類中使用 |
| override | 重寫父類或接口中的成員 | 若沒有final聲明,重寫的成員默認(rèn)是open |
Java中可以創(chuàng)建任意類的子類并重寫任意方法,除非顯示聲明final。對(duì)基類的修改會(huì)導(dǎo)致子類不正確的行為,即脆弱的基類問題。Effective Java建議“要么為繼承做好設(shè)計(jì)并記錄文檔,要么禁止。”Kotlin采用該思想哲學(xué),Java中類和方法默認(rèn)是open的,而Kotlin中類和方法默認(rèn)是final。
open class RichButtion:Clickable{
//默認(rèn)是final不能被重寫
fun disable(){}
//open可重寫
open fun animate(){}
//override 方法默認(rèn)是open
override fun click(){}
}
接口和抽象類默認(rèn)是open, 其抽象成員默認(rèn)是open
abstract class Animate{
//默認(rèn)是open
abstract fun animate()
//非抽象方法默認(rèn)是final
fun animateTwice(){}
}
可見性修飾符
| 修飾符 | 類成員 | 頂層聲明 |
|---|---|---|
| public(默認(rèn)) | 所有地方可見 | 所有地方可見 |
| internal | 模塊中可見 | 模塊中可見 |
| protected | 子類中可見 | —— |
| private | 類中可見 | 文件中可見 |
Java中默認(rèn)可見性——包私有,在kotlin中并沒有。Kotlin只把包作為命名空間里組織代碼的一種方式,并沒有將其用作可見性控制。作為替代方案,Koltin是使用新的修飾符internal,表示“只能在模塊內(nèi)可見?!?code>internal優(yōu)勢(shì)在于它對(duì)模塊實(shí)現(xiàn)細(xì)節(jié)提供真正的封裝。
接口
接口包含抽象方法的定義和非抽象方法的實(shí)現(xiàn),但是他們都不能包含任何狀態(tài)。
interface Clickable {
//不支持backing-field,不能存儲(chǔ)值
var clickable: Boolean
//默認(rèn)open,可被重寫
fun click()
//默認(rèn)final,不能被重寫
fun showOff() = println("I'm Clickable")
}
由于Koltin 1.0是Java 6為目標(biāo)設(shè)計(jì),其并不支持接口中的默認(rèn)方法,因此會(huì)把每個(gè)默認(rèn)方法的接口編譯成一個(gè)普通接口和一個(gè)將方法體作為靜態(tài)函數(shù)的類的結(jié)合體,如上面的接口反編譯后看到:
public interface Clickable {
boolean getClickable();
void setClickable(boolean var1);
void click();
void showOff();
public static final class DefaultImpls {
public static void showOff(Clickable $this) {
String var1 = "I'm Clickable";
boolean var2 = false;
System.out.println(var1);
}
}
}
構(gòu)造函數(shù)
Kotlin構(gòu)造函數(shù)相對(duì)于Java做了部分修改,區(qū)分主構(gòu)造函數(shù)和從構(gòu)造函數(shù)。初始化塊中的代碼實(shí)際上會(huì)成為主構(gòu)造函數(shù)的?部分。委托給主構(gòu)造函數(shù)會(huì)作為次構(gòu)造函數(shù)的第?條語(yǔ)句,因此所有初始化塊中的代碼都會(huì)在次構(gòu)造函數(shù)體之前執(zhí)?。
class Person {
init {
println("Init block")
}
constructor(i: Int) {
println("Constructor")
}
}
在大多數(shù)場(chǎng)景中,類的構(gòu)造函數(shù)非常簡(jiǎn)明:要么沒有參數(shù),要么直接把參數(shù)于對(duì)應(yīng)的屬性關(guān)聯(lián)
class User(val nickname:String,val isSubscribed:Boolean=false)
如果類有主構(gòu)造函數(shù),每個(gè)從構(gòu)造函數(shù)需要委托主構(gòu)造函數(shù),可直接委托或者間接委托。
class User(val nickname: String) {
var isSubscribed: Boolean?=null
constructor(_nickname: String, _isSubscribed: Boolean) : this(_nickname) {
this.isSubscribed = _isSubscribed
}
}
如何該類有父類,應(yīng)該顯式的調(diào)用父類的構(gòu)造方法
//Clickable為接口,沒有構(gòu)造函數(shù)
class Buttion:Clickable{
}
//即便沒有任何參數(shù),也要顯示調(diào)用父類構(gòu)造函數(shù)
class RiseButton:Button(){
}
//如果有多級(jí)構(gòu)造函數(shù),可以super關(guān)鍵字調(diào)用父類構(gòu)造
class MyButton: View {
constructor(ctx:Context):super(ctx)
constructor(ctx: Context,attributes: AttributeSet?):super(ctx,attributes)
}
內(nèi)部類、嵌套類、密封類、數(shù)據(jù)類·
內(nèi)部類和嵌套類
Kotlin中嵌套類不能訪問外部類的實(shí)例,類似Java靜態(tài)內(nèi)部類;而Kotlin中的內(nèi)部類需要用inner關(guān)鍵字修飾才能訪問外部類的實(shí)例。
class Outer {
private val bar: Int = 1
//內(nèi)部類
inner class Inner {
fun foo() = bar
}
}
class Outer2 {
private val bar: Int = 1
//嵌套類,不持有外部類的引用
class Nested {
fun foo() = 2
}
}
val demo = Outer().Inner().foo() // == 1
val demo2 = Outer2.Nested().foo() // == 2
密封類
密封類?來表?受限的類繼承結(jié)構(gòu):當(dāng)?個(gè)值為有限集中的類型、?不能有任何其他類型時(shí)。在某種意義上,他們是枚舉類的擴(kuò)展:枚舉類型的值集合也是受限的,但每個(gè)枚舉常量只存在?個(gè)實(shí)例,?密封類的?個(gè)?類可以有可包含狀態(tài)的多個(gè)實(shí)例。
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
?個(gè)密封類是??抽象的,它不能直接實(shí)例化并可以有抽象(abstract)成員。
密封類不允許有?-private 構(gòu)造函數(shù)(其構(gòu)造函數(shù)默認(rèn)為 private)。
請(qǐng)注意,擴(kuò)展密封類?類的類(間接繼承者)可以放在任何位置,??需在同?個(gè)?件中。
數(shù)據(jù)類
創(chuàng)建?些只保存數(shù)據(jù)的類。 在這些類中,?些標(biāo)準(zhǔn)函數(shù)往往是從數(shù)據(jù)機(jī)械推導(dǎo)?來的。在
Kotlin 中,這叫做 數(shù)據(jù)類 并標(biāo)記為 data :
data class User(val name: String, val age: Int)
編譯器?動(dòng)從主構(gòu)造函數(shù)中聲明的所有屬性導(dǎo)出以下成員:
-
equals() / hashCode()對(duì); -
toString()格式是 "User(name=John, age=42)" ; -
componentN()函數(shù) 按聲明順序?qū)?yīng)于所有屬性; - copy() 函數(shù)。
為了確保?成的代碼的?致性以及有意義的?為,數(shù)據(jù)類必須滿?以下要求:
- 主構(gòu)造函數(shù)需要?少有?個(gè)參數(shù);
- 主構(gòu)造函數(shù)的所有參數(shù)需要標(biāo)記為 val 或 var ;
- 數(shù)據(jù)類不能是抽象、開放、密封或者內(nèi)部的;
屬性與字段
聲明一個(gè)屬性的完整語(yǔ)法為:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其初始器(initializer)、getter 和 setter 都是可選.
一個(gè)只讀屬性的語(yǔ)法和一個(gè)可變的屬性的語(yǔ)法有兩方面的不同:
- 只讀屬性用
val,而可變屬性用var聲明 - 只讀屬性不允許有
setter方法
默認(rèn)的屬性的聲明為:
var name: String = "Kotlin"
get() = field
set(value) {
field = value
}
Object關(guān)鍵字
Object關(guān)鍵字定義一個(gè)類并同時(shí)創(chuàng)建一個(gè)實(shí)體:
- 對(duì)象聲明:定義單例的方式
- 伴生對(duì)象:可持有工廠方法及其他與類相關(guān)
- 對(duì)象表達(dá)式:代替
Java的匿名內(nèi)部類
對(duì)象表達(dá)式和對(duì)象聲明之間有?個(gè)重要的語(yǔ)義差別:
- 對(duì)象表達(dá)式是在使?他們的地??即執(zhí)?(及初始化)的;
- 對(duì)象聲明是在第?次被訪問到時(shí)延遲初始 化的;
- 伴?對(duì)象的初始化是在相應(yīng)的類被加載(解析)時(shí),與 Java 靜態(tài)初始化器的語(yǔ)義相匹配。
對(duì)象聲明
對(duì)象聲明將類的聲明與該類的單一實(shí)例聲明結(jié)合在一起。與普通類的實(shí)例不同,對(duì)象聲明在定義的時(shí)候就創(chuàng)建了實(shí)例。
object PayRoll {
val allEmployees = arrayListOf<Person>()
fun calculateSalary(){
}
}
可以反編譯看到:
對(duì)象聲明被編譯成通過靜態(tài)字段來持有它的單一實(shí)例的類,字段名始終為INSTANCE
public final class PayRoll {
@NotNull
private static final ArrayList allEmployees;
public static final PayRoll INSTANCE;
@NotNull
public final ArrayList getAllEmployees() {
return allEmployees;
}
public final void calculateSalary() {
}
//構(gòu)造函數(shù)私有
private PayRoll() {
}
static {
PayRoll var0 = new PayRoll();
//靜態(tài)代碼塊初始化化實(shí)例對(duì)象
INSTANCE = var0;
boolean var1 = false;
allEmployees = new ArrayList();
}
}
伴生對(duì)象
Java的static關(guān)鍵字并不是kotlin的一部分,作為替代,kotlin依賴包級(jí)別的函數(shù)和對(duì)象聲明,但是頂層函數(shù)不能訪問類的私有成員, 需要寫一個(gè)沒有類實(shí)例情況下調(diào)用但需要訪問類內(nèi)部的函數(shù),可以將其寫為類中的對(duì)象聲明的成員。
fun getFacebookName(accountId: Int) = "fb:$accountId"
class User private constructor(val nickname: String) {
companion object {
fun newSubscribingUser(email: String) =
User(email.substringBefore('@'))
fun newFacebookUser(accountId: Int) =
User(getFacebookName(accountId))
}
}
fun main(args: Array<String>) {
val subscribingUser = User.newSubscribingUser("bob@gmail.com")
val facebookUser = User.newFacebookUser(4)
println(subscribingUser.nickname)
}
伴生對(duì)象作為普通對(duì)象,一樣可以實(shí)現(xiàn)接口和擴(kuò)展函數(shù)和屬性
data class Person(val name: String) {
object NameComparator : Comparator<Person> {
override fun compare(p1: Person, p2: Person): Int =
p1.name.compareTo(p2.name)
}
}
class Person(val firstname:String,val lastname:String){
companion object{
//...可空,但不能省略
}
}
fun Person.Companion.fromJson(json:String):String{
return json.substring(4)
}
對(duì)象表達(dá)式
object不僅可用來聲明單例對(duì)象,還可以聲明匿名對(duì)象,替代java內(nèi)部類的用法
fab.setOnClickListener(
object : View.OnClickListener {
override fun onClick(view: View?) {
//....
}
})
當(dāng)然,也可以將其存儲(chǔ)到一個(gè)變量中:
val listener = object : View.OnClickListener {
override fun onClick(p0: View?) {
//....
}
}
Java匿名內(nèi)部類只能擴(kuò)展一類或者實(shí)現(xiàn)一個(gè)接口,kotlin的匿名對(duì)象可以實(shí)現(xiàn)多個(gè)接口或者實(shí)現(xiàn)不同的接口。