概要
- 類(lèi)
- 在聲明
接口&抽象類(lèi)&枚舉&注解
時(shí)梗搅,與 Java 相比基本一樣,除了枚舉&注解
在聲明的時(shí)候后面喲啊加上class
; - 構(gòu)造函數(shù)的簡(jiǎn)單實(shí)現(xiàn)基本一致,還有高級(jí)用法哦食棕!
- 在實(shí)現(xiàn)
繼承&實(shí)現(xiàn)
時(shí)都是通過(guò)冒號(hào)
呐萌,而且接口和類(lèi)不區(qū)分先后順序馁痴; - 獲取
class
是通過(guò)雙冒號(hào)的形式; - Kotlin 的
Any
類(lèi)似于 Java 的Object
; - 三種內(nèi)部類(lèi):
object
的匿名肺孤;默認(rèn)的靜態(tài)罗晕;inner
的嵌套济欢; - Kotlin 的
this
的形式為this.@ClassName.this
; - 僅當(dāng)前模塊可見(jiàn)的
internal
;
- 在聲明
- 函數(shù)
- 無(wú)返回值用
Unit
; - 默認(rèn)生成的
setter&getter
小渊,@JvmField
標(biāo)記的作用法褥;
- 無(wú)返回值用
- 變量
- 可空類(lèi)型和不可空類(lèi)型
- 數(shù)組
- 空安全設(shè)計(jì)
lateinit
- 類(lèi)型轉(zhuǎn)換
- 非空斷言
- 編譯器常量
- 其他
- 頂層函數(shù)&
object
&伴生對(duì)象三種方式實(shí)現(xiàn)靜態(tài)函數(shù)與變量,使用@JvmStatic
標(biāo)記用static
修飾酬屉; - 字符串的
${}
和"""
半等; - 區(qū)間
前者..后者
,前者包含后者不包含呐萨; - 條件分支支持表達(dá)式的
switch
高級(jí)版when`杀饵; - Kotlin 不強(qiáng)制捕獲異常;
- 簡(jiǎn)單的循環(huán)谬擦;
- 注釋
[]
凹髓; -
open | final
;
- 頂層函數(shù)&
類(lèi)
接口 & 抽象類(lèi) & 枚舉 & 注解
// 接口
interface KotlinInterface { ...}
// 抽象類(lèi)
abstract class KotlinAbstractClass { ...}
// 枚舉「java 僅用 enum 一個(gè)關(guān)鍵字就行了」
enum class KotlinEnum { ...}
// 注解「java 是 @interface 」
annotation class KotlinAnotation { ...}
構(gòu)造函數(shù)
// 例一:無(wú)參構(gòu)造 & 有參構(gòu)造
class KotlinInfo {
var str1: String? = null
var str2: String? = null
// 函數(shù)體無(wú)邏輯怯屉,大括號(hào)是可以省略的
constructor() {}
constructor(str1: String?, str2: String?) {
this.str1 = str1
this.str2 = str2
}
}
// 例二:重寫(xiě)父類(lèi)構(gòu)造
// 發(fā)現(xiàn)沒(méi)蔚舀?繼承父類(lèi) View 時(shí)后面沒(méi)有寫(xiě)括號(hào),因?yàn)橹貙?xiě)了父類(lèi)的構(gòu)造函數(shù)锨络,其實(shí)還有簡(jiǎn)寫(xiě)的方式
class KotlinView : View {
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
}
繼承 & 實(shí)現(xiàn)
都是通過(guò):
來(lái)修飾赌躺,通過(guò),
來(lái)分隔類(lèi)與接口,接口與類(lèi)的前后順序不受限制羡儿。
class KotlinActivity : AppCompatActivity(), View.OnClickListener{}
獲取 class
// 獲取 kotlin 的 class
KotlinActivity::class
// 獲取 java 的 class
JavaActivity::class.java
Object & Any
在 Java 中所有類(lèi)都是Object
的子類(lèi)礼患,Kotlin 則是Any
,Any
其實(shí)相當(dāng)于就是 Java 中的Object
掠归;
兩者相比缅叠,Any
比Object
更精簡(jiǎn),只有三個(gè)方法equals()
hashCode()
toString()
虏冻。
內(nèi)部類(lèi)
匿名內(nèi)部類(lèi)
使用object
關(guān)鍵字實(shí)現(xiàn)匿名內(nèi)部類(lèi)
// 接口是沒(méi)有構(gòu)造函數(shù)的
// 因此在實(shí)現(xiàn) OnClickListener 時(shí)后面是不需要小括號(hào)的
val button = Button(context)
button.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View?) {
}
})
靜態(tài)內(nèi)部類(lèi)
Kotlin 中默認(rèn)內(nèi)部類(lèi)就是靜態(tài)內(nèi)部類(lèi)肤粱,因此可以將函數(shù)修飾為靜態(tài)而不修改類(lèi);
嵌套內(nèi)部類(lèi)
既然默認(rèn)就是靜態(tài)內(nèi)部類(lèi)厨相,那如何修改成嵌套內(nèi)部類(lèi)呢领曼?
class StaticInnerClass { ...} // 默認(rèn)為靜態(tài)內(nèi)部類(lèi)
inner class InnerClass { ...} // 修改為嵌套內(nèi)部類(lèi)
this
// Java 通過(guò)「類(lèi)名.this」獲取目標(biāo)類(lèi)引用
MainActivity.this.finish();
// Kotlin 通過(guò)「this@類(lèi)名」獲取目標(biāo)類(lèi)引用
this@MainActivity.finish()
可見(jiàn)性
Kotlin 默認(rèn)可見(jiàn)修飾符為public
,Kotlin 沒(méi)有包訪問(wèn)權(quán)限蛮穿;
新增internal
可見(jiàn)性修飾符代表當(dāng)前模塊可見(jiàn)庶骄;
如:組件開(kāi)發(fā)時(shí)當(dāng)前 Moudle 可見(jiàn);做 SDK 或開(kāi)源庫(kù)時(shí)對(duì)當(dāng)前模塊可見(jiàn)践磅。
函數(shù)
函數(shù)聲明
無(wú)返回值在 Java 是void
单刁;Kotlin 是Unit
;無(wú)返回值時(shí)Unit
是可以省略的府适;
// 無(wú)入?yún)⒏岱伞o(wú)返回值
fun main(): Unit { ...}
// 有入?yún)⒎握痢⒂蟹祷刂?fun addNum(num1: Int, num2: Int): Int {
return num1 + num2
}
setter & getter
在聲明變量時(shí) Kotlin 默認(rèn)就生成了setter
&getter
方法,注意val
只會(huì)生成getter
褥傍。
但是如果我們需要在setter
&getter
里面做一些處理咋辦呢儡嘶?
class KotlinInfo{
var str: String? = null
set(value) {
// field 為本身
field = value
}
get() {
return field
}
// 我在 java 中調(diào)用時(shí)不想要 set&get 方法喇聊,就想直接調(diào)用變量
// kotlin 默認(rèn)生成的 set&get
// 在 java 中使用這個(gè)變量時(shí)只能通過(guò) set&get
// 在 kotlin 中使用不能調(diào)用 set&get恍风,默認(rèn)就是 set&get
}
擴(kuò)展:在 Kotlin 我們都是直接點(diǎn)出變量名來(lái)取值與賦值,Kotlin 默認(rèn)生成的setter
&getter
在 Java 中是能用的誓篱,但是即使變量名是公共的朋贬,我們?nèi)匀灰仓荒?code>setter&getter
來(lái)調(diào)用該變量。
Java 如何像 Kotlin 一樣使用變量呢窜骄?或者說(shuō)如何點(diǎn)出變量而不是通過(guò)setter
&getter
锦募?
使用@JvmField
注解可以解決這一痛點(diǎn),但是這樣 Java 調(diào)用時(shí)就不會(huì)有setter
&getter
了邻遏。
@JvmField // 通過(guò) @JvmField 注解后糠亩,在 java 中調(diào)用就能直接使用變量,這樣就不會(huì)有 set&get 了
var str: String? = null
變量
var & val
Kotlin 通過(guò)var
&val
兩個(gè)關(guān)鍵字來(lái)定義變量准验;
-
var
:可讀可寫(xiě)變量赎线; -
val
:只讀變量,相當(dāng)于 Java 里final
修飾過(guò)的變量糊饱;
var age: Int = 25
val name: String = "Kotlin"
val kotlin: Kotlin = Kotlin()
在平時(shí)定義的時(shí)候垂寥,一般后面的類(lèi)型都是可以省略的;
Kotlin 與 Java 一樣都是靜態(tài)類(lèi)型的語(yǔ)言另锋,一旦等號(hào)右邊類(lèi)型確定則無(wú)法再更改變量類(lèi)型滞项;
因此變量類(lèi)型只是省略掉了,不代表沒(méi)有類(lèi)型夭坪,只是 Kotlin 做了類(lèi)型推斷文判。
var age = 25
val name = "Kotlin"
val kotlin = Kotlin()
數(shù)組
定義數(shù)組用arrayOf
var arrayStr = arrayOf("kotlin", "java")
arrayOf
會(huì)自動(dòng)對(duì)內(nèi)容類(lèi)型做轉(zhuǎn)換,但是這樣會(huì)存在問(wèn)題室梅;
arrayOf
在進(jìn)行轉(zhuǎn)換時(shí)律杠,會(huì)進(jìn)行拆箱裝箱操作,所以其實(shí)他的類(lèi)型是包裝類(lèi)型竞惋,這樣就會(huì)存在性能消耗柜去;
所以一般我們要指定數(shù)組內(nèi)容類(lèi)型。
var arrayInt = intArrayOf(1,2,3)
var arrayFloat = floatArrayOf(1f,2f)
空安全
類(lèi)型
空安全設(shè)計(jì)是 Kotlin 設(shè)計(jì)的一大亮點(diǎn)拆宛,空安全上比 Java 更安全原因嗓奢,便是因?yàn)樗目瞻踩O(shè)計(jì);
- 可空類(lèi)型:在變量類(lèi)型后面通過(guò)
?
修飾浑厚;var str: String? = null
str
是可空類(lèi)型股耽,允許為null
根盒; - 不可空類(lèi)型:默認(rèn)就是;
var str: String = "text"
str
是不可空類(lèi)型物蝙,不允許為null
炎滞,編譯時(shí)就會(huì)報(bào)錯(cuò);
在 Java 中雖然也有 @Nullable
&@NotNull
可以標(biāo)記诬乞,但是在 Java 中只會(huì)有警告提示册赛,并不會(huì)在編譯期間受影響;
調(diào)用符
在使用可空類(lèi)型時(shí)還會(huì)涉及到兩個(gè)調(diào)用符
-
!!
強(qiáng)行調(diào)用符:強(qiáng)制執(zhí)行震嫉,不管變量是否為空森瘪,如為空會(huì)拋出空指針異常; -
?.
安全調(diào)用符:安全調(diào)用票堵,相當(dāng)于加了層是否為空校驗(yàn)扼睬,不為空才調(diào)用;
maybeNull!!.setText("text")
maybeNull?.setText("text")// if (maybeNull != null){ maybeNull?.setText("text"); }
平臺(tái)類(lèi)型
那么不可空類(lèi)型就一定不會(huì)為空嗎悴势?
var button: Button
button = findViewById(R.id.btn)
// button 是雖然是不可空類(lèi)型窗宇,但是仍然會(huì)有空指針的風(fēng)險(xiǎn)
// 我們一行寫(xiě)出來(lái)是這樣的
var button: Button! = findViewById<Button>(R.id.btn)
// Button! 實(shí)際在編碼時(shí)是隱藏提示的,可是他又是什么呢特纤?
// 在類(lèi)型后面加一個(gè)感嘆號(hào)這就是一個(gè)平臺(tái)類(lèi)型
// 我們來(lái)看下 findViewById 的代碼
@Nullable
public abstract <T extends View> T findViewById(@IdRes int id);
// 所以實(shí)際 findViewById 返回的對(duì)象是可能為空的
lateinit
在使用延遲初始化「lateinit」時(shí)有兩個(gè)前提
- 被修飾的變量必須是不可控類(lèi)型
- 被修飾的變量不能有初始值
在使用時(shí)可以減少大量?
的使用军俊,減少?zèng)]必要的可空類(lèi)型使用;
或者也可以這么理解:我這個(gè)不可空類(lèi)型變量需要晚點(diǎn)賦值叫潦,一開(kāi)始你要允許我為空蝇完,待會(huì)我肯定給你賦值就行
// 不加 lateinit 是這樣的
class KotlinActivity : AppCompateActivity(){
private var button: Button? = null
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
button = findViewById(R.id.button)
button?.setText("btn")
}
}
// 加了呢
class KotlinActivity : AppCompateActivity(){
private lateinit var button: Button
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
button = findViewById(R.id.button)
button.setText("text")
}
}
類(lèi)型轉(zhuǎn)換
數(shù)據(jù)類(lèi)型轉(zhuǎn)換
var floatNum: Float = 200f // 直接賦值為 float 類(lèi)型
var floatNum: Float = 200.toFloat() // 調(diào)用函數(shù)轉(zhuǎn)換
在 Kotlin 中一切皆對(duì)象,包括數(shù)據(jù)類(lèi)型也是矗蕊,這里還有些裝箱之類(lèi)的概念短蜕,如var floatNum: Float? = 200f
這里就有一個(gè)裝箱操作。
類(lèi)型強(qiáng)轉(zhuǎn)
使用as
關(guān)鍵字進(jìn)行類(lèi)型強(qiáng)轉(zhuǎn)
var view: View? = null
// is 相當(dāng)于 Java 的 instanceof
if (view is Button) {
// as 相當(dāng)于 Java 的強(qiáng)轉(zhuǎn)傻咖,(Button)view
Button button = view as Button
button.setText("text")
}
// Kotlin 在這里又體現(xiàn)了智能的地方
// 當(dāng)你的 view 對(duì)象校驗(yàn)已經(jīng)是 Button 了朋魔,它就認(rèn)為不需要你在強(qiáng)轉(zhuǎn)了
if (view is Button) {
view.setText("text")
}
非空斷言
可空類(lèi)型強(qiáng)轉(zhuǎn)類(lèi)型為不可空類(lèi)型,可以在變量后面加上!!
來(lái)達(dá)到類(lèi)型裝換卿操。
編譯器常量
概念:在編譯期就確定了值且以后不再發(fā)生變化警检。
在靜態(tài)變量前加上const
關(guān)鍵字變成編譯器常量;注意一定是在靜態(tài)變量前害淤!
其他
靜態(tài)
頂層函數(shù)
又叫包級(jí)函數(shù)扇雕,表示在 Kotlin 文件中直接申明的變量或函數(shù),而這些變量或函數(shù)又都是靜態(tài)的窥摄。
package com.youaji.util
// 靜態(tài)變量
private val displayMetrics = Resources.getSystem().displayMetrics
// 靜態(tài)函數(shù)
fun dp2px(dp: Float): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, displayMetrics)
}
使用的時(shí)候直接使用函數(shù)名或變量名镶奉,導(dǎo)包連同包名與函數(shù)名或變量名一起導(dǎo)入;
這樣會(huì)存在一些問(wèn)題,如重載的時(shí)候或多個(gè)文件一樣的函數(shù)名哨苛,雖然導(dǎo)包的時(shí)候能區(qū)別鸽凶,但識(shí)別度很不友好;這個(gè)問(wèn)題先留著建峭,留著等下解決玻侥,先來(lái)解決緊急點(diǎn)的問(wèn)題,是不是 Java 調(diào)用也是一樣呢亿蒸?
// Kotlin 調(diào)用
import com.youaji.util.dp2px
val pxValue = dp2px(10f)
// Java 調(diào)用凑兰,雖然是通過(guò)類(lèi)調(diào)用的,但是類(lèi)名變成了 UtilKt
UtilKt.dp2px(10f);
// 好好的一個(gè) Util 一定要寫(xiě)成 UtilKt 才能使用祝懂,想個(gè)辦法改改
@file:JvmName("Utils")
package com.youaji.util
// Java 調(diào)用就變成這樣了
Utils.dp2px(10f);
// 如果文件名是 Util.kt票摇,那就不能設(shè)置成 Util拘鞋,設(shè)置了也沒(méi)用砚蓬,原因還不知道,猜測(cè)如果你設(shè)置成與文件名一樣也就沒(méi)意義了盆色,或者說(shuō)這樣也會(huì)與文件名沖突
// 雖然說(shuō)能自定義名字灰蛙,但是實(shí)際認(rèn)為大多數(shù)場(chǎng)景是用不到的,so隔躲,一般情況也沒(méi)必要去改
雖然可以通過(guò)@file:JvmName("name")
修改名字摩梧,但是還是有些不爽,我文件名是Util.kt
是不能設(shè)置名字為Util
的宣旱,就算設(shè)置了也不生效仅父,具體原因還沒(méi)深究,猜測(cè)其實(shí)這樣設(shè)置成與文件名一直也沒(méi)意義浑吟,也有可能存在文件名沖突問(wèn)題笙纤,所以,雖然能自定義组力,但是實(shí)際開(kāi)發(fā)是大多數(shù)場(chǎng)景是用不到的省容,一般情況也沒(méi)必要去修改。
object
前面通過(guò)object
實(shí)現(xiàn)了匿名內(nèi)部類(lèi)燎字,那這里object
其實(shí)還能修飾靜態(tài)類(lèi)腥椒,還能實(shí)現(xiàn)單例呢!也解決了上面不能通過(guò)類(lèi)名調(diào)用函數(shù)的疑問(wèn)候衍。
object Util {
private val displayMetrics = Resources.getSystem().displayMetrics
fun dp2px(dp: Float): Float {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,displayMetrics)
}
}
// Kotlin 使用笼蛛;這樣就跟 java 一樣,通過(guò)類(lèi)名調(diào)用函數(shù)
Util.dp2px(10f)
// Java 使用蛉鹿;有差異滨砍,咋個(gè)多了個(gè) INSTANCE 呢!其實(shí)是因?yàn)閱卫脑?Util.INSTANCE.dp2px(10f);
伴生對(duì)象
// 僅做示范呦
class Util {
constructor() { displayMetrics = Resources.getSystem().displayMetrics}
companion object {
private lateinit var displayMetrics: DisplayMetrics
fun getDisplayMetrics(): DisplayMetrics { return displayMetrics}
}
}
Util.getDisplayMetrics() // kotlin 使用
Util.Companion.getDisplayMetrics(); // java 使用
擴(kuò)展
-
疑問(wèn)一:為什么 Kotlin 把
static
關(guān)鍵字去掉呢?這個(gè)其實(shí)挺矛盾的惨好,面向?qū)ο笾谢蛙睿f(wàn)物接對(duì)象,但是被 static 修飾的又不屬于哪個(gè)對(duì)象日川,Kotlin 想避免這種情況蔓腐,但是頂層函數(shù)的存在又有些沖突;
-
疑問(wèn)二:Kotlin 中沒(méi)有
static
龄句,那這些靜態(tài)方法是不是就是 Java 中被static
修飾的方法呢回论?不是的,Kotlin 只是用這些方式更優(yōu)雅的替代了 Java 中使用
static
的場(chǎng)景分歇,但是在與 Java 混編的時(shí)候還是可以通過(guò)@JvmStatic
標(biāo)記為被static
修飾傀蓉。
@JvmStatic
fun dp2px(dp: Float): Float { ...}
@JvmStatic
fun getDisplayMetrics(): DisplayMetrics { ...}
// Kotlin 中使用仍是一樣,Java 中跟使用 static 方法一樣职抡,無(wú)需使用 INSTANCE 或 Companion
Util.dp2dp(10f)
Util.getDisplayMetrics();
字符串
字符串模板
通過(guò)${}
形式使用字符串模板
var data = "2019-07-21"
var number = 666
var str = "${data}收到轉(zhuǎn)賬${number}元"
// 如果是單一的變量葬燎,大括號(hào)是可以省略的
var str = "收到轉(zhuǎn)賬$number元"
多行字符串
通過(guò)"""
代替\n
拼接的多行字符串
var str = """
第一行
第二行
第三行
"""
區(qū)間
// 一般我們做區(qū)間判斷是這么寫(xiě)
if (num >= 200 && num < 300) { }
// 但是我們有了區(qū)間就可以這樣寫(xiě)
if (num in 200..299) { }
// 什么意思呢?表示 num 是否在包含 200 且小于 300 的區(qū)間當(dāng)中
when
when 相當(dāng)與 Java 中的高級(jí)版switch
缚甩,因?yàn)?when 在條件分支上支持表達(dá)式谱净。
// 一般使用,相當(dāng)于 java 的 switch
when (num) {
200 -> println("200")
300 -> println("300")
400 -> println("400")
else -> println("其他")
}
// 配合區(qū)間的表達(dá)式
when (num) {
in 200..299 -> println(">= 200 < 300")
in 300..399 -> println(">= 300 < 400")
in 400..499 -> println(">= 400 < 500")
500 -> println("500")
else -> println("其他")
}
異常捕獲
在 Kotlin 中是不需要強(qiáng)制捕獲異常的 try - catch
循環(huán)
簡(jiǎn)單使用下循環(huán)
val kotlinInfos = ArrayList<KotlinInfo>()
// java 使用
for (KotlinInfo kotlinInfo : kotlinInfos) { ...}
// kotlin 使用
for (kotlinInfo in kotlinInfos) { ...}
注釋
注釋中可以在任意地方使用[]
來(lái)引用目標(biāo)擅威,代替@param
@link
等壕探。
open | final
Kotlin 中的類(lèi)和函數(shù),默認(rèn)是被final
修飾的郊丛,但是abstract
和override
是例外.
問(wèn)題
基本可以上手了李请,在學(xué)習(xí)總結(jié)的同時(shí)仍然存在一些疑問(wèn)!
- setter & getter
- 什么情況默認(rèn)生成厉熟?
- 為什么可以省略
setter&getter
直接訪問(wèn)變量导盅? - 為什么有些不會(huì)提示你可以直接使用變量?
- 為什么 Kotin 要舍棄
static
庆猫?而 Kotlin 的靜態(tài)函數(shù)又不像 Java 的static
呢认轨? - 為什么 Kotlin 不需要強(qiáng)制捕獲異常?