一喘垂、條件判斷
1、when精講
在 Java 中有 switch 語(yǔ)句绍撞,在 Kotlin 中使用 when
來(lái)代替 switch正勒。同時(shí) when 也可以代替 if 。你以為 when 只是用來(lái)代替 switch 和 if 的嗎傻铣?其實(shí)遠(yuǎn)遠(yuǎn)不止這些章贞,其中還包含了一些不為認(rèn)知的小秘密。下面我們都會(huì)為大家一一揭曉非洲。
1.1鸭限、when的基本語(yǔ)法
when(parameter){
branch1 -> logic
branch2 -> logic
}
when 括號(hào)里是參數(shù),參數(shù)是可選的两踏。箭頭(->) 左邊是條件分支败京,右邊是對(duì)應(yīng)的邏輯體
when 不需要向 switch 那樣需要加上 break 語(yǔ)句,符合條件自動(dòng)具有 break
功能
如果邏輯體代碼比較多梦染,可以放到花括號(hào) {}
里:
when(parameter){
branch1 -> {
//...
}
branch1 -> {
//...
}
}
如果要組合多個(gè)分支赡麦,可以使用逗號(hào)(,)分隔分支:
when(parameter){
branch1,branch1 -> {
//...
}
}
1.2、枚舉類(lèi)對(duì)象作為when參數(shù)
fun getMnemonic(color: Color) = when (color) {
Color.RED -> "Richard"
Color.ORANGE -> "Of"
Color.YELLOW -> "York"
Color.GREEN -> "Gave"
Color.BLUE -> "Battle"
Color.INDIGO -> "In"
Color.VIOLET -> "Vain"
}
需要注意的是弓坞,when 使用枚舉對(duì)象作為參數(shù)隧甚,需要把該枚舉類(lèi)的所有對(duì)象列舉完
所以 枚舉對(duì)象作為 when 參數(shù)不需要 else 分支
1.3、任意對(duì)象作為when參數(shù)
Kotlin 中的 when 比 Java 中的 switch 功能更強(qiáng)大
Java 的 switch 參數(shù)只能是 枚舉常量渡冻、字符串戚扳、整型或整型的包裝類(lèi)型(浮點(diǎn)型不可以)
Kotlin 的 when 可以是任意對(duì)象:
fun mix(c1: Color, c2: Color) = when (setOf(c1, c2)) {
setOf(RED, YELLOW) -> ORANGE
setOf(YELLOW, BLUE) -> GREEN
setOf(BLUE, VIOLET) -> INDIGO
//需要處理 其他 情況
else -> throw Exception("Dirty color")
}
1.4、無(wú)參數(shù)的when表達(dá)式
上面的 mix 函數(shù)比較低效族吻,因?yàn)槊看伪容^的時(shí)候都會(huì)創(chuàng)建一個(gè)或多個(gè) set 集合
如果該函數(shù)調(diào)用頻繁帽借,會(huì)創(chuàng)建很多臨時(shí)對(duì)象
可以使用無(wú)參的 when 表達(dá)式來(lái)改造下:
fun mixOptimized(c1: Color, c2: Color) = when {
(c1 == RED && c2 == YELLOW) || (c1 == YELLOW && c2 == RED) ->
ORANGE
(c1 == YELLOW && c2 == BLUE) || (c1 == BLUE && c2 == YELLOW) ->
GREEN
(c1 == BLUE && c2 == VIOLET) || (c1 == VIOLET && c2 == BLUE) ->
INDIGO
else -> throw Exception("Dirty color")
}
無(wú)參數(shù)的 when 表達(dá)式的條件分支必須是 boolean 類(lèi)型
1.5珠增、智能類(lèi)型轉(zhuǎn)換(smart-casts)
在 Java 中對(duì)某個(gè)對(duì)象進(jìn)行類(lèi)型轉(zhuǎn)換的時(shí)候時(shí)候,需要通過(guò) instanceof 來(lái)判斷是否可以被強(qiáng)轉(zhuǎn)
void test(Object obj) {
if (obj instanceof String) {
String str = (String) obj;
str.substring(0, str.length() / 2);
}
//...
}
Kotlin 通過(guò) is 關(guān)鍵字來(lái)判斷類(lèi)型砍艾,并且編譯器會(huì)自動(dòng)幫你做類(lèi)型轉(zhuǎn)換
fun test(obj: Any) {
if (obj is String) {
// 不需要手動(dòng)做類(lèi)型轉(zhuǎn)換操作
obj.substring(0, obj.length / 2)
}
//...
}
1.6蒂教、when原理分析
例如下面的程序:
fun testWhen(index: Int) {
when (index) {
0 -> {
println("0")
}
1, 2 -> {
println("1,2")
}
}
}
也就是說(shuō),當(dāng) index = 1 或者 2 都會(huì)執(zhí)行 println("1,2")
我們對(duì)上面的例子進(jìn)行反編譯:
public final void testWhen(int index) {
String var2;
boolean var3;
switch(index) {
case 0:
var2 = "0";
var3 = false;
System.out.println(var2);
break;
case 1:
case 2:
var2 = "1,2";
var3 = false;
System.out.println(var2);
}
}
發(fā)現(xiàn)脆荷,它底層還是通過(guò) Java 的 switch 來(lái)實(shí)現(xiàn)的凝垛。我們對(duì)上面的 kotlin 案例進(jìn)行小的修改:
fun testWhen(index: Int) {
when (index) {
0 -> {
println("0")
}
1, 2 -> {
println("1,2")
}
in 4..10 -> {
println(4..10)
}
}
}
在反編譯可以看出,發(fā)生了變化(變成了 if):
public final void testWhen(int index) {
String var3;
boolean var4;
if (index == 0) {
var3 = "0";
var4 = false;
System.out.println(var3);
} else if (index != 1 && index != 2) {
if (4 <= index) {
if (10 >= index) {
byte var5 = 4;
IntRange var6 = new IntRange(var5, 10);
var4 = false;
System.out.println(var6);
}
}
} else {
var3 = "1,2";
var4 = false;
System.out.println(var3);
}
}
當(dāng)我們加上了 in 4..10
條件蜓谋,那么底層則無(wú)法通過(guò) switch 來(lái)實(shí)現(xiàn)了梦皮,所以只能轉(zhuǎn)而使用了 if 來(lái)實(shí)現(xiàn)。
1.7桃焕、when fallthrough
在 Java switch 是支持 fallthrough 的:
static void test(String value) {
switch (value) {
case "one":
System.out.println("1");
case "two":
System.out.println("2");
break;
case "three":
System.out.println(3);
break;
}
}
test("one") 會(huì)輸出 1,2
但在 Kotlin 中不支持 when 的 fallthrough剑肯,因?yàn)镴ava 中在使用 switch 的如果默認(rèn)是 fallthrough ,需要顯式的加上 break观堂,這樣容易產(chǎn)生bug让网,如上面的 Java 代碼。
所以在Kotlin 中 when 是不支持 fallthrough 的师痕。
1.8溃睹、when的程序健壯性
經(jīng)過(guò)上面的分析我們知道,在 Kotlin 中使用 when 來(lái)代替 switch 或 if胰坟,除此以外丸凭,他們還有哪些不同嗎?好腕铸,我們來(lái)看下下面的一個(gè) Java switch 例子:
public void test(String value){
switch (value){
case "hello":
System.out.println("hello");
break;
case "world":
System.out.println("world");
break;
default:
System.out.println("unknown");
}
}
Java 基礎(chǔ)比較好的都知道,上面的代碼有可能會(huì)拋出 NullPointException铛碑,當(dāng)我們調(diào)用 test 方法的時(shí)候傳遞的參數(shù)為 null 時(shí)狠裹,就會(huì)拋出異常,因?yàn)樵?Java 中對(duì) String 進(jìn)行 switch 本質(zhì)上是使用了 string.hashCode 方法汽烦。 下面我們使用 Kotlin 來(lái)改寫(xiě)上的例子:
fun test(value: String?) {
when (value) {
"hello" -> println("hello")
"world" -> println("world")
else -> println("unknown")
}
}
我們?cè)賮?lái)看下反編譯的結(jié)果:
public final void test(@Nullable String value) {
String var3;
boolean var4;
if (value != null) {
switch(value.hashCode()) {
case 99162322:
if (value.equals("hello")) {
var3 = "hello";
var4 = false;
System.out.println(var3);
return;
}
break;
case 113318802:
if (value.equals("world")) {
var3 = "world";
var4 = false;
System.out.println(var3);
return;
}
}
}
var3 = "unknown";
var4 = false;
System.out.println(var3);
}
從上面的反編譯后的代碼不難看出涛菠,在對(duì) switch case
之前,進(jìn)行 if 判空處理撇吞,所以就算 test
方法參數(shù)為 null
也不會(huì)出現(xiàn)空指針異常俗冻。
可以看出,相似的代碼邏輯牍颈,使用 Kotlin 來(lái)實(shí)現(xiàn)要比 Java 來(lái)實(shí)現(xiàn)要健壯的多迄薄。其實(shí)這只是 Kotlin 優(yōu)勢(shì)的冰山一角,隨著學(xué)習(xí)的深入煮岁,我想你會(huì)越來(lái)越喜歡 Kotlin讥蔽。明白這些底層原理涣易,我相信你對(duì)自己的 Kotlin 代碼越來(lái)越自信,因?yàn)槟阒滥銓?xiě)的每行 Kotlin 代碼在底層意味著什么冶伞。
1.9新症、when總結(jié)
至此,when
的介紹就要告一段落了响禽。Kotlin 使用 when 統(tǒng)一了條件判斷徒爹,當(dāng)然經(jīng)典的 if 也可以用作條件判斷,但是在 Kotlin 中更多的是使用 when 來(lái)進(jìn)行條件分支的判斷芋类,if 在 Kotlin 有它獨(dú)有的應(yīng)用的地方隆嗅。同時(shí) when 相對(duì)于 Java 中的 switch 來(lái)說(shuō),減少了很多程序員容易調(diào)入的陷阱梗肝,增加了程序的穩(wěn)定性榛瓮。除此以外,when 還有一些東西可以講巫击,這個(gè)我們留到介紹 枚舉
的時(shí)候再來(lái)細(xì)聊禀晓,同時(shí)也會(huì)結(jié)合實(shí)際工作中的一些思考 ,來(lái)聊聊 when 和 枚舉 結(jié)合使用的情況和問(wèn)題坝锰,這個(gè)相對(duì)于只介紹語(yǔ)法來(lái)說(shuō)更加重要粹懒。
2、if
if 表達(dá)式 用于條件判斷顷级,在 Kotlin 中 如果判斷分支比較多凫乖,通常使用 when 來(lái)替代 if,如:
fun test(obj: Any) {
when (obj) {
is String -> obj.substring(0, obj.length / 2)
is Type2 -> ignore
is Type3 -> ignore
}
}
if 還可以實(shí)現(xiàn)三目運(yùn)算符:
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}
二弓颈,循環(huán)
1帽芽、while,do...while
Kotlin 中的 while 和 do...while 循環(huán)和 Java 沒(méi)有什么區(qū)別
while (condition) {
/*...*/
}
do {
/*...*/
} while (condition)
2翔冀、for循環(huán)
..
操作符
for 循環(huán)的語(yǔ)法和 Java 中的循環(huán)還是有些區(qū)別
// Java for 循環(huán)
for (int i = 0; i <= 100; i++) {
System.out.println(i);
}
// 對(duì)應(yīng) Kotlin 版本
for(i in 0..100){
println(i)
}
使用 ..
操作符 表示一個(gè)區(qū)間导街,該區(qū)間是閉區(qū)間,包含開(kāi)始和結(jié)束的元素纤子。
in搬瑰、until操作符
然后使用 in 操作符來(lái)遍歷這個(gè)區(qū)間,這個(gè)區(qū)間是從小到大的控硼,如果開(kāi)始的數(shù)字比結(jié)尾的還要大泽论,則沒(méi)有意義
如果想要表示 半閉區(qū)間 ,即 只包含頭部元素卡乾,不包含尾部翼悴,可以使用 until 操作符:
for(i in 0 until 100){
println(i)
}
in、downTo操作符
如果想要倒序遍歷说订,可以使用 downTo 關(guān)鍵字:
for(i in 100 downTo 0){
println(i)
}
反編譯后:
int i = 100;
for(boolean var3 = false; i >= 0; --i) {
boolean var4 = false;
System.out.println(i);
}
// 其實(shí)就相當(dāng)于遞減:
for(int i = 100; i >= 0; --i) {
System.out.println(i);
}
// 輸出 100 到 0 之間的數(shù)
遍歷的時(shí)候 步長(zhǎng)(step) 默認(rèn)是 1抄瓦,可以通過(guò) step 關(guān)鍵字來(lái)指定步長(zhǎng)
for( i in 100 downTo 0 step 2){
println(i)
}
操作符 ..
和 downTo
表示區(qū)間都是閉區(qū)間潮瓶,包含首尾元素的。
三钙姊、小結(jié)
如果是針對(duì)集合進(jìn)行遍歷毯辅,for 循環(huán)也可以使用 forEach 進(jìn)行遍歷
list.forEach {
println(it.name)
}