修飾符
訪問修飾符
修飾符 | 相關(guān)成員 | 評注 |
---|---|---|
final | 不能被重寫 | 類中成員默認(rèn)使用 |
open | 可被重寫 | 需要明確什么 |
abstract | 必須被重寫 | 在抽象類中使用 |
override | 重寫父類或接口中的成員 | 若沒有final聲明止吁,重寫的成員默認(rèn)是open |
Java
中可以創(chuàng)建任意類的子類并重寫任意方法什乙,除非顯示聲明final
挽封。對基類的修改會導(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)可見⊥ú”internal
優(yōu)勢在于它對模塊實(shí)現(xiàn)細(xì)節(jié)提供真正的封裝毕莱。
接口
接口包含抽象方法的定義和非抽象方法的實(shí)現(xiàn),但是他們都不能包含任何狀態(tài)颅夺。
interface Clickable {
//不支持backing-field朋截,不能存儲值
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)方法,因此會把每個默認(rèn)方法的接口編譯成一個普通接口和一個將方法體作為靜態(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ù)相對于Java
做了部分修改廓八,區(qū)分主構(gòu)造函數(shù)和從構(gòu)造函數(shù)。初始化塊中的代碼實(shí)際上會成為主構(gòu)造函數(shù)的?部分胆描。委托給主構(gòu)造函數(shù)會作為次構(gòu)造函數(shù)的第?條語句瘫想,因此所有初始化塊中的代碼都會在次構(gòu)造函數(shù)體之前執(zhí)?。
class Person {
init {
println("Init block")
}
constructor(i: Int) {
println("Constructor")
}
}
在大多數(shù)場景中昌讲,類的構(gòu)造函數(shù)非常簡明:要么沒有參數(shù)国夜,要么直接把參數(shù)于對應(yīng)的屬性關(guān)聯(lián)
class User(val nickname:String,val isSubscribed:Boolean=false)
如果類有主構(gòu)造函數(shù),每個從構(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(){
}
//如果有多級構(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)?個值為有限集中的類型棵逊、?不能有任何其他類型時伤疙。在某種意義上,他們是枚舉類的擴(kuò)展:枚舉類型的值集合也是受限的辆影,但每個枚舉常量只存在?個實(shí)例徒像,?密封類的?個?類可以有可包含狀態(tài)的多個實(shí)例。
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
?個密封類是??抽象的蛙讥,它不能直接實(shí)例化并可以有抽象(abstract)成員锯蛀。
密封類不允許有?-private 構(gòu)造函數(shù)(其構(gòu)造函數(shù)默認(rèn)為 private)。
請注意次慢,擴(kuò)展密封類?類的類(間接繼承者)可以放在任何位置谬墙,??需在同?個?件中。
數(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)
編譯器?動從主構(gòu)造函數(shù)中聲明的所有屬性導(dǎo)出以下成員:
-
equals() / hashCode()
對; -
toString()
格式是 "User(name=John, age=42)" 傅蹂; -
componentN()
函數(shù) 按聲明順序?qū)?yīng)于所有屬性纷闺; - copy() 函數(shù)。
為了確保?成的代碼的?致性以及有意義的?為份蝴,數(shù)據(jù)類必須滿?以下要求:
- 主構(gòu)造函數(shù)需要?少有?個參數(shù)犁功;
- 主構(gòu)造函數(shù)的所有參數(shù)需要標(biāo)記為 val 或 var ;
- 數(shù)據(jù)類不能是抽象婚夫、開放浸卦、密封或者內(nèi)部的;
屬性與字段
聲明一個屬性的完整語法為:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
其初始器(initializer)案糙、getter 和 setter 都是可選.
一個只讀屬性的語法和一個可變的屬性的語法有兩方面的不同:
- 只讀屬性用
val
,而可變屬性用var
聲明 - 只讀屬性不允許有
setter
方法
默認(rèn)的屬性的聲明為:
var name: String = "Kotlin"
get() = field
set(value) {
field = value
}
Object關(guān)鍵字
Object
關(guān)鍵字定義一個類并同時創(chuàng)建一個實(shí)體:
- 對象聲明:定義單例的方式
- 伴生對象:可持有工廠方法及其他與類相關(guān)
- 對象表達(dá)式:代替
Java
的匿名內(nèi)部類
對象表達(dá)式和對象聲明之間有?個重要的語義差別:
- 對象表達(dá)式是在使?他們的地??即執(zhí)?(及初始化)的限嫌;
- 對象聲明是在第?次被訪問到時延遲初始 化的;
- 伴?對象的初始化是在相應(yīng)的類被加載(解析)時时捌,與 Java 靜態(tài)初始化器的語義相匹配怒医。
對象聲明
對象聲明將類的聲明與該類的單一實(shí)例聲明結(jié)合在一起。與普通類的實(shí)例不同奢讨,對象聲明在定義的時候就創(chuàng)建了實(shí)例稚叹。
object PayRoll {
val allEmployees = arrayListOf<Person>()
fun calculateSalary(){
}
}
可以反編譯看到:
對象聲明被編譯成通過靜態(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í)例對象
INSTANCE = var0;
boolean var1 = false;
allEmployees = new ArrayList();
}
}
伴生對象
Java
的static
關(guān)鍵字并不是kotlin
的一部分,作為替代扒袖,kotlin
依賴包級別的函數(shù)和對象聲明塞茅,但是頂層函數(shù)不能訪問類的私有成員, 需要寫一個沒有類實(shí)例情況下調(diào)用但需要訪問類內(nèi)部的函數(shù),可以將其寫為類中的對象聲明的成員僚稿。
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)
}
伴生對象作為普通對象凡桥,一樣可以實(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)
}
對象表達(dá)式
object不僅可用來聲明單例對象蚀同,還可以聲明匿名對象缅刽,替代java內(nèi)部類的用法
fab.setOnClickListener(
object : View.OnClickListener {
override fun onClick(view: View?) {
//....
}
})
當(dāng)然,也可以將其存儲到一個變量中:
val listener = object : View.OnClickListener {
override fun onClick(p0: View?) {
//....
}
}
Java
匿名內(nèi)部類只能擴(kuò)展一類或者實(shí)現(xiàn)一個接口蠢络,kotlin
的匿名對象可以實(shí)現(xiàn)多個接口或者實(shí)現(xiàn)不同的接口衰猛。