1. 可見性
- private 只在該類(以及它的成員)中可見
- protected 和 private 一樣但在子類中也可見
- internal 在本模塊的所有可以訪問到聲明區(qū)域的均可以訪問該類的所有 internal 成員
- public 任何地方可見 當沒寫明可見性,默認為public
- 特別的:外部類不可以訪問內(nèi)部類的 private 成員。
以上模塊是指
- an IntelliJ IDEA module;
- a Maven or Gradle project;
2. 構(gòu)造函數(shù)
類可以包含一個主構(gòu)造函數(shù)和多個二級構(gòu)造函數(shù),完整聲明如下。如果構(gòu)造函數(shù)不包括可見性聲明和注解,則constructor可以省略
class Customer public @inject constructor (name: String) {...}
主構(gòu)造函數(shù)不能包含任意代碼,初始化代碼放在init{}代碼塊內(nèi)
主構(gòu)造函數(shù)可以直接初始化域變量,也可以(必須)直接帶入超類的參數(shù)中
class Person(val firstName: String, val lastName: String, var age: Int) : BasePerson(firstName, lastName) {
}
二級構(gòu)造函數(shù)需要constructor前綴
如果存在主構(gòu)造函數(shù),每一個二級構(gòu)造函數(shù)都要直接調(diào)用,或間接通過另一個二級構(gòu)造函數(shù)代理調(diào)用主構(gòu)造函數(shù),用this關鍵字來指代其他構(gòu)造函數(shù)
class Person(val name: String) {
constructor (name: String, paret: Person) : this(name) {
parent.children.add(this)
}
}
構(gòu)造函數(shù)中的參數(shù)可以有默認值,類似于python,當有默認值的參數(shù),new的時候,可以忽略而使用默認值
實際上Kotlin創(chuàng)建對象不使用new 直接使用 Person("Wang")
3. Kotlin的接口
Kotlin的接口允許包含了方法的實現(xiàn),但和抽象類不同的是,接口不能保存狀態(tài)。如下,
- C實現(xiàn)A接口,bar()方法是抽象的,則強制需要重寫實現(xiàn)它。
- D同時實現(xiàn)A和B,對于bar()方法,因為B已經(jīng)包含了實現(xiàn),所以會默認選擇B的實現(xiàn);但因為A和B都包含了對foo()這個方法的實現(xiàn),造成了沖突,所以強制重寫此方法。
interface A {
fun foo() { print("A") }
fun bar()
}
interface B {
fun foo() { print("B") }
fun bar() { print("bar") }
}
class C : A {
override fun bar() { print("bar") }
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}
4. 類繼承和方法屬性復寫
- kotlin 中堅持做明確的事,Kotlin的類默認下都是final的,需要加上open關鍵字才可被繼承。而抽象類和接口則天生是open的。
- 對于可以復寫的方法,Kotlin也需要明確使用open來聲明,并且重寫他們
- override的成員也是open的,如果想終結(jié)這個open,需要加final
- 可以用var去覆蓋一個val,但反之則不允許
- 當繼承的類和實現(xiàn)的接口包含同名同參方法,并且均有實現(xiàn)時,需要重新復寫這個方法(參考接口)
5. 屬性
屬性可以有g(shù)et set方法
var stringRepresentation: String
get() = this.toString()
set(value) {
setDataFromString(value) // parses the string and assigns values to other properties
}
備份域:
var counter = 0 // the initializer value is written directly to the backing field
set(value) {
if (value >= 0)
field = value
}
備份屬性
private var _table: Map<String, Int>? = null
public val table: Map<String, Int>
get() {
if (_table == null)
_table = HashMap() // Type parameters are inferred
return _table ?: throw AssertionError("Set to null by another thread")
}
對于成員屬性,不管是空或非空,都不許再定義的時候初始化。
如果想延遲初始化,有以下方法:
val a : Any by Delegates.notNull()
var b : Any by Delegates.notNull()
lateinit var c : Any
val d : Any? by lazy { }
val e : Any by lazy { }
6. 枚舉類
- 實際上每一個枚舉都是一個對象實例。
- 此外,枚舉類實現(xiàn)了Comparable接口,比較適使用的是枚舉類的自然順序進行比較
// 1 普通枚舉類
enum class Direction {
NORTH,SOUTH,WEST,EAST
}
// 2 可初始化的枚舉類
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
// 3 可以聲明枚舉實例自己的匿名類
enum class SwitchState {
ON {
override fun switch() = OFF
},
OFF{
override fun switch() = ON
};
abstract fun switch(): SwitchState
}
7. 密封類 sealed
- 密封類相當于一個枚舉類的擴展:枚舉值集合的類型是嚴格限制的,但每個枚舉常量只有一個實例,而密封類的子類可以有包含不同狀態(tài)的多個實例。
- 密封類主要的好處體現(xiàn)在when表達式,可以確保聲明覆蓋所有情形而不需要else
sealed class Expr {
class Const(val number: Double) : Expr()
class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
}
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
// the `else` clause is not required because we've covered all the cases
}
8. 代理屬性
有些常用屬性,其讀寫方法可以進行抽象(類似于Gson的屬性封裝),get set方法可以通過代理類進行封裝,以后可以重用該代理。
class Delegate {
fun get(thisRef: Any?, prop: PropertyMetadata): String {
return "$thisRef, thank you for delegating '${prop.name}' to me !"
}
fun set(thisRef: Any?, prop: PropertyMatada, value: String) {
println("$value has been assigned to '${prop.name} in $thisRef.'")
}
}
class Example {
var p: String by Delegate()
}
Kotlin提供的標準代理
kotlin.properties.Delegates 對象是標準庫提供的一個工廠方法并提供了很多有用的代理
// 懶加載
val lazy: String by Delegates.lazy { // 如果你想要線程安全,使用 blockingLazy()
println("computed!")
"Hello"
}
// 觀察者
var name: String by Delegates.observable("default") {
d,old,new -> println("$old -> $new")
}
// NotNull
// 正常來說 非空的var不允許不初始化,也不允許用null初始化
class Foo {
var bar: Bat //錯誤必須初始化
}
// 使用Delegates.notNull()可以作為替代進行null初始化,但如果在set之前get,則會拋出異常
class Foo {
var bar: Bar by Delegates.notNull()
}
// 在Map中解析屬性
class User(val map: Map<String, Any?>) {
val name: String by Delegates.mapVal(map) // var 則可以用 mapVar
val age: Int by Delegates.mapVal(map)
}
9. 代理類
Kotlin提供代理類的方式去快速實現(xiàn)代理模式
interface Base {
fun print()
}
class BaseImpl(val x: Int) : Base {
override fun print() { printz(x) }
}
class Derived(b: Base) : Base by b
fun main() {
val b = BaseImpl(10)
Derived(b).print()
}
10. 嵌套類
嵌套類可分為
- 一般嵌套類:這個跟Java差不多,不展開講。區(qū)別是無法引用外部類的屬性和方法
- 內(nèi)部類:用inner修飾,可以引用外部類的屬性和方法。但外部類無法使用內(nèi)部類的private屬性或方法
- 匿名內(nèi)部類:使用對象表達式創(chuàng)建,參考下一條“對象聲明”
class Outer {
private val bar: Int = 1
// 內(nèi)部類
inner class Inner {
fun foo() = bar
}
}
11. 對象聲明
Kotlin中有3種可以進行聲明的對象
- 匿名內(nèi)部類
- 單例對象
- 伴隨對象
// 類似于匿名內(nèi)部類
button.setOnClickListener( object : OnClickListener {
override fun onClick(v: View) {
// do something
}
})
// 單例
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
// ...
}
}
// 伴隨對象 其中聲明的方法和屬性相當于是java的類里面聲明的靜態(tài)屬性和靜態(tài)方法,可以使用類名來直接引用
class MyClass {
companion object : Factory<MyClass> {
override fun create(): MyClass = MyClass()
}
}
以上單例類似于java中的
public final class DataProviderManager {
private DataProviderManager() {}
public static DataProviderManager INSTANCE = new DataProviderManager();
void registerDataProvider(DataProvider provider) {
// ...
}
}
12. 多重聲明與data類
針對常用的數(shù)據(jù)存儲類Java bean,Kotlin提供了更便捷的data類。data類的限制如下
- 主構(gòu)造函數(shù)至少有一個參數(shù)
- 所有構(gòu)造函數(shù)參數(shù)必須標注為val或var,即不能是局部變量
- 數(shù)據(jù)類不能使abstract open sealed 或 inner
- 不能繼承,但能實現(xiàn)接口
- 如果在 JVM 中如果構(gòu)造函數(shù)是無參的,則所有的屬性必須有默認的值
- data class User(val name: String = "", val age: Int = 0)
data class User(val name: String, val age: Int)
// toString() of the form "User(name=John, age=42)";
// copy() 便于原型模式的使用
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
// componentN() 便于多重聲明和多重返回,對應構(gòu)造參數(shù)的聲明順序
val (name, age) = person
val name = persion.component1()
val age = persion.component2()
data class Result(val result: Int, val status: Status)
fun function(...): Result {
//...
return Result(result, status)
}
val (result, status) = function(...)
- Kotlin標準庫提供了 Pair 和 Triple 兩種標準data類,但可讀性不強不建議使用。其實可以作為多參數(shù)返回時使用。
13. 類的擴展
在 java 中,我們通常使用一系列名字為 "Utils" 的類: FileUtils,StringUtils等等。很有名的 java.util.Collections 也是其中一員的,但我們不得不像下面這樣使用他們:
Collections.swap(list,
Collections.binarySearch(list, Collections.max(otherList)),
Collections.max(list));
Kotlin類的擴展可以讓我們可以使用以下方式去引用這樣的工具類靜態(tài)方法
list.swap(list.binarySearch(otherList.max()), list.max())
如對MutableList<T>使用類的擴展:
fun <T> MutableList<T>.swap(x: Int, y: Int) {
val temp = this[x] // this 對應 list
this[x] = this[y]
this[y] = tmp
}
類的擴展需要注意以下幾點:
- 類的擴展是被靜態(tài)解析的,如果有同名同參數(shù)的成員函數(shù)和擴展函數(shù),調(diào)用的時候必然會使用成員函數(shù)
- 被擴展的類甚至可以為Any?
- 可擴展的包括 方法、屬性、伴隨對象
- 擴展的作用域 僅當前包有效。其他包要使用某個擴展需要進行引用,如以上擴展,需要執(zhí)行如下導入:
import x.x.x.swap