扔物線老師的原文章_那些不是那么寫的
跟著走一遍,加深印象和理解
構造器
- 需要加
constructor
標識 - 沒有public修飾, 默認可見性就是公開的
class User {
var name: String = ""
constructor(name: String) {
this.name = name
}
}
init
- 都在實例化時執(zhí)行滓技,并且執(zhí)行順序都在構造器之前
java
public class User {
??
{
// 初始化代碼塊,先于下面的構造器執(zhí)行
}
public User() {
}
}
???
class User {
??
init {
// 初始化代碼塊呻粹,先于下面的構造器執(zhí)行
}
constructor() {
}
}
final
- kotlin 中的
val
和 final類似 - kotlin 函數參數默認是
val
類型, 所以參數錢不需要寫val
關鍵字
val自定義getter
val
和 final
有一點區(qū)別: 雖然val修飾的變量不能二次賦值, 但可以通過自定義變量的getter
函數, 讓變量每次被訪問時,返回動態(tài)獲取的值
val size: Int
get() { // ?? 每次獲取 size 值時都會執(zhí)行 items.size
return items.size
}
static property /function
- 在 Kotlin 里秀又,靜態(tài)變量和靜態(tài)方法這兩個概念被去除了
companion object
class Sample {
...
??
companion object {
val anotherString = "Another String"
}
}
object (kotlin的單例)
- Java 中的
Object
在 Kotlin 中變成了Any
流济,和Object
作用一樣:作為所有類的基類萌壳。 - 而
object
不是類, 在kotlin中是和calss一樣屬于關鍵字 -
object
的意思: 創(chuàng)建一個類,并且創(chuàng)建一個這個類的對象,在代碼中使用這個對象,可以直接通過類名訪問, - 創(chuàng)建一個kotlin的單例, 只需要將
class
換成object
-
object
繼承和實現接口都可(簡單來講, 將class關鍵字的功能和單例合二為一)
和java相比不同點:
- 和類的定義類似莉钙,但是把 class 換成了 object 嵌莉。
- 不需要額外維護一個實例變量 sInstance进萄。
- 不需要「保證實例只創(chuàng)建一次」的 getInstance() 方法。
匿名類
Java的寫法 ??
ViewPager.SimpleOnPageChangeListener listener = new ViewPager.SimpleOnPageChangeListener() {
@Override // ??
public void onPageSelected(int position) {
// override
}
};
Kotlin的寫法 ??
val listener = object: ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
// override
}
}
Kotlin
- 和java很相似, 只是將
new
換成了object
- Java 中 new 用來創(chuàng)建一個匿名類的對象
- Kotlin 中
object:
也可以用來創(chuàng)建匿名類的對象 - 這里的
new
和object:
修飾的都是接口或者抽象類锐峭。
companion object
- 用
object
修飾對象中的變量和函數都是靜態(tài)的
只想讓類中的一部分函數和變量是靜態(tài),寫法??
class A {
object B {
var c: Int = 0
}
}
訪問可以直接 A.B.c
類中嵌套的對象可以用 companion
修飾
-
當有 companion 修飾時中鼠,對象的名字也可以省略掉
一個類中只能有一個伴生對象
top-level property / function 聲明
top-level declaration
頂層聲明」
- 其實就是把屬性和函數的聲明不寫在 class 里面
package com.example.kotlinstudy
fun topTest(){
}
引用topTest()
- 這樣寫的屬性和函數,不屬于任何
class
,直接屬于package
- 和靜態(tài)變量,靜態(tài)函數一樣是全局的,使用的時候可不寫類名
- 相同命名的頂級函數, IDE會自動加上包前綴區(qū)分
object
沿癞、companion object
和 top-level
選擇對比
- 如果想寫工具類的功能援雇,直接創(chuàng)建文件,寫 top-level「頂層」函數椎扬。
- 如果需要繼承別的類或者實現接口惫搏,就用 object 或 companion object。
常 量
class Sample {
companion object {
?? // ??
const val CONST_NUMBER = 1
}
}
數組和集合
java寫法
String[] strs = {"a", "b", "c"};
Kotlin寫法
val strs: Array<String> = arrayOf("a", "b", "c")
//獲取:
strs[0]
//修改:
strs[1]="B"
kotlin中的數組是一個擁有泛型的類,創(chuàng)建函數也是泛型函數,和集合數據類型一樣
kotlin數組的工具函數
get() / set()
contains()
first()
find()
- 不支持協(xié)變
Kotlin 的數組編譯成字節(jié)碼時使用的仍然是 Java 的數組蚕涤,但在語言層面是泛型實現筐赔,這樣會失去協(xié)變 (covariance) 特性,就是子類數組對象不能賦值給父類的數組變量
// Kotlin:
val strs: Array<String> = arrayOf("a", "b", "c")
val anys: Array<Any> = strs // compile-error: Type mismatch
//而這在 Java 中是可以的:
String[] strs = {"a", "b", "c"};
Object[] objs = strs; // success
集合
Kotlin 和 Java 一樣有三種集合類型:List揖铜、Set 和 Map
-
list
以固定順序存儲一組元素茴丰,元素可以重復。 -
Set
存儲一組互不相等的元素天吓,通常沒有固定順序贿肩。 -
Map
存儲 鍵-值 對的數據集合,鍵互不相等龄寞,但不同的鍵可以對應相同的值尸曼。
list
//java 寫法
List<String> strList = new ArrayList<>();
strList.add("a");
//kotlin寫法
val strList = listOf("a", "b", "c")
Kotlin中的List
支持協(xié)變,可把子類的list賦值給父類的list變量
val strs: List<String> =listOf("a", "b", "c")
val anys: List<Any> =strs //success
而這在java中是會報錯的
- 對于協(xié)變的支持與否,List 和數組剛好反過來
和數組的區(qū)別
這個問題在 Java 中就存在了萄焦,數組和 List 的功能類似,List 的功能更多一些,直覺上應該用 List 拂封。但數組也不是沒有優(yōu)勢茬射,基本類型 (int[]、float[]) 的數組不用自動裝箱冒签,性能好一點在抛。
在 Kotlin 中也是同樣的道理,在一些性能需求比較苛刻的場景萧恕,并且元素類型是基本類型時刚梭,用數組好一點。不過這里要注意一點票唆,Kotlin 中要用專門的基本類型數組類 (IntArray FloatArray LongArray) 才可以免于裝箱朴读。也就是說元素不是基本類型時,相比 Array走趋,用 List 更方便些衅金。
Set
//java寫法
Set<String> strSet = new HashSet<>();
strSet.add("a");
//Kotlin寫法
val strSet = setOf("a", "b", "c")
- 和 List 類似,Set 同樣具有 covariant(協(xié)變)特性簿煌。
Map
//java寫法
Map<String, Integer> map = new HashMap<>();
map.put("key1", 1);
//kotlin寫法
val map=mapOf("key1" to 1, "key2" to 2,"key3" to 3)
mapOf
的每個參數表示一個鍵值對氮唯,to
表示將「鍵」和「值」關聯,這個叫做「中綴表達式」
取值和修改
- kotlin中的Map除了和 Java 一樣可以使用 get() 根據鍵獲取對應的值姨伟,還可以使用方括號的方式獲瘸土稹:
val value1 = map.get("key1")
val value2 = map["key2"]
- 類似的,kotlin中也可以用方括號方式改變
Map
鍵對應的值
val map = mutableMapOf("key1" to 1, "key2" to 2)
map.put("key1", 2)
map["key1"] = 2
- 你這里用到了「操作符重載」的知識,實現了和數組一樣的「Positional Access Operations」
可變集合/不可變集合
上面修改Map
值的例子中夺荒,創(chuàng)建函數用的是 mutableMapOf()
而不是 mapOf()
瞒渠,因為只有 mutableMapOf()
創(chuàng)建的 Map
才可以修改。Kotlin 中集合分為兩種類型:
只讀的和可變的般堆。這里的只讀有兩層意思:
- 集合的size不可變
- 集合中的元素值不可變
以下是三種集合類型創(chuàng)建不可變和可變實例的例子:
listOf()
創(chuàng)建不可變的 List
在孝,mutableListOf()
創(chuàng)建可變的 List
。
setOf()
創(chuàng)建不可變的 Set
淮摔,mutableSetOf()
創(chuàng)建可變的 Set
私沮。
mapOf()
創(chuàng)建不可變的 Map
,mutableMapOf()
創(chuàng)建可變的 Map
和橙。
mutable(單詞意思可變的)前綴創(chuàng)建的函數都是可變的, 沒有mutable前綴都不可變, 但是可以通過toMutable*()
系函數轉換成可變的集合
val strList = listOf("a", "b", "c")
strList.toMutableList() //轉換成可變
val strSet = setOf("a", "b", "c")
strSet.toMutableSet() //轉換成可變
val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 3)
map.toMutableMap() //轉換成可變
- toMutable*() 返回的是一個新建的集合仔燕,原有的集合還是不可變的,所以只能對函數返回的集合修改魔招。
Sequence
kotlin引入的新的容器類型 Sequence
,和 Iterable
一樣用 來遍歷一組數據并可以對每個元素進行特定的處理
創(chuàng)建
1. 類似listOf()
,使用一組元素創(chuàng)建
sequenceOf("a", "b", "c")
2. 使用Iterable
創(chuàng)建
val list=listOf("a", "b", "c")
list.asSequence()
3. 使用 lambda 表達式創(chuàng)建
val sequence= generateSequence(0) {it +1}
可見性修飾符
kotlin的四種可見性修飾符
-
public
:公開晰搀,可見性最大,哪里都可以引用办斑。 -
private
:私有外恕,可見性最小杆逗,根據聲明位置不同可分為類中可見和文件中可見。 -
protected
:保護鳞疲,相當于 private + 子類可見罪郊。 -
internal
:內部,僅對 module 內可見尚洽。
相比 Java 少了一個 default
「包內可見」修飾符悔橄,多了一個internal
「module 內可見」修飾符。這一節(jié)結合例子講講 Kotlin 這四種可見性修飾符腺毫,以及在 Kotlin 和 Java 中的不同癣疟。
public
Kotlin 中如果不寫可見性修飾符,就表示公開潮酒,和 Java 中 public
修飾符具有相同效果睛挚。在 Kotlin 中 public
修飾符「可以加,但沒必要」澈灼。
@ hide
在 Android 的官方 sdk 中竞川,有一些方法只想對 sdk 內可見,不想開放給用戶使用(因為這些方法不太穩(wěn)定叁熔,在后續(xù)版本中很有可能會修改或刪掉)委乌。為了實現這個特性,會在方法的注釋中添加一個 Javadoc 方法 @hide
荣回,用來限制客戶端訪問:
* @hide ??
*/
public void hideMethod() {
...
}
但這種限制不太嚴格遭贸,可以通過反射訪問到限制的方法。針對這個情況心软,Kotlin 引進了一個更為嚴格的可見性修飾符:internal
internal
internal 表示修飾的類壕吹、函數僅對 module 內可見,這里的 module 具體指的是一組共同編譯的 kotlin 文件删铃,常見的形式有:
- Android Studio 里的 module
- Maven project
internal
在寫一個 library module 時非常有用耳贬,當需要創(chuàng)建一個函數僅開放給 module 內部使用,不想對 library 的使用者可見猎唁,這時就應該用 internal 可見性修飾符咒劲。
Java 的「包內可見」怎么沒了?
Java 的 default
「包內可見」在 Kotlin 中被棄用掉了诫隅,Kotlin 中與它最接近的可見性修飾符是internal
「module 內可見」腐魂。為什么會棄用掉包內可見呢?我覺得有這幾個原因:
- Kotlin 鼓勵創(chuàng)建 top-level 函數和屬性逐纬,一個源碼文件可以包含多個類蛔屹,使得 Kotlin 的源碼結構更加扁平化,包結構不再像 Java 中那么重要豁生。
- 為了代碼的解耦和可維護性兔毒,module 越來越多漫贞、越來越小,使得 internal 「module 內可見」已經可以滿足對于代碼封裝的需求育叁。
protected
- Java 中
protected
表示包內可見 + 子類可見绕辖。 - Kotlin 中
protected
表示private
+ 子類可見。
Kotlin 相比 Java protected
的可見范圍收窄了擂红,原因是 Kotlin 中不再有「包內可見」的概念了,相比 Java 的可見性著眼于 package
围小,Kotlin 更關心的是 module
昵骤。
private
Java 中的 private
表示類中可見,作為內部類時對外部類「可見」肯适。
Kotlin 中的 private
表示類中或所在文件內可見变秦,作為內部類時對外部類「不可見」。
private
修飾的變量「類中可見」和 「文件中可見」:
class Sample {
private val propertyInClass = 1 // ?? 僅 Sample 類中可見
}
private val propertyInFile = "A string." // ?? 范圍更大框舔,整個文件可見
private
修飾內部類的變量時蹦玫,在 Java 和 Kotlin 中的區(qū)別:
- 在 Java 中,外部類可以訪問內部類的 private 變量:
//java代碼示例
public class Outter {
public void method() {
Inner inner = new Inner();
??
int result = inner.number * 2; // success
}
private class Inner {
private int number = 0;
}
}
- 在 Kotlin 中刘绣,外部類不可以訪問內部類的 private 變量:
//kotlin示例代碼
class Outter {
fun method() {
val inner = Inner()
??
val result = inner.number * 2 // compile-error: Cannot access 'number': it is private in 'Inner'
}
class Inner {
private val number = 1
}
}
- 可以修飾類和接口
- Java 中一個文件只允許一個外部類樱溉,所以 class 和 interface 不允許設置為 private,因為聲明 private 后無法被外部使用纬凤,這樣就沒有意義了福贞。
- Kotlin 允許同一個文件聲明多個 class 和 top-level 的函數和屬性,所以 Kotlin 中允許類和接口聲明為 private停士,因為同個文件中的其它成員可以訪問:
private class Sample {
val number = 1
fun method() {
println("Sample method()")
}
}
// ?? 在同一個文件中挖帘,所以可以訪問
val sample = Sample()