Kotlin 是基于 JVM 的语言。它提供了许多语法糖来替代 Java 中的一些操作。如果 Java 做不到的功能,Kotlin 也做不到。Kotlin 源代码执行时都会被编译为Java字节码,IDE中可以使用 Tool -> Kotlin->Show Kotlin Bytecode查看自己的代码将会被编译之后的样子,然后使用IDE的 Decompile 反编译成java源码查看,就知道Kotlin代码实际对应的Java代码了。

部分内容吸取自 https://github.com/heimashi/kotlin_tips

字符串

  • 三个引号可以包含换行特殊字符
1
2
3
4
val str1 = "abcd"
val str2 = """
{"name":"tom", "age": 20}
""".trimIndent()

发编译为Java:

1
String str2 = StringsKt.trimIndent("\n    {\"name\":\"tom\", \"age\": 20}\n    ");
  • 模板字符串
1
2
3
data class User(val name: String, val age: Int)
val user = User("tom", 20)
println("${user.name}'s age is ${user.age}")

反编译为Java:

1
2
3
User user = new User("tom", 20);
String var7 = "" + user.getName() + "'s age is " + user.getAge();
System.out.println(var7);

如果要想显示 $符,需要使用反斜杠 \转义

控制结构 -> 表达式

  • 表达式有返回值,可以作为其他表达式的一部分
  • 控制结构,编程语句,负责包围代码块,本身没有值

if 表达式

1
2
3
fun max(a: Int, b: Int): Int {
return if (a > b) a else b
}

反编译为Java:

1
2
3
public final int max(int a, int b) {
return a > b ? a : b;
}

when 表达式

可替换 switch 和 if-else 语句

简化 switch

1
2
3
4
5
6
7
fun getDesc(value: Int) = when (value) {
90 -> "GOOD"
80 -> "OK"
70 -> "GOON"
60 -> "Simple"
else -> "DEFAULT"
}

反编译的Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public final String getDesc(int value) {
String var10000;
switch(value) {
case 60:
var10000 = "Simple";
break;
case 70:
var10000 = "GOON";
break;
case 80:
var10000 = "OK";
break;
case 90:
var10000 = "GOOD";
break;
default:
var10000 = "DEFAULT";
}
return var10000;
}

简化 if-else

1
2
3
4
5
fun getDesc2(value: Int) = when {
value >= 90 -> "GOOD"
value in 60..80 -> "OK"
else -> "BAD"
}

反编译的Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public final String getDesc2(int value) {
String var10000;
if (value >= 90) {
var10000 = "GOOD";
} else {
if (60 <= value) {
if (80 >= value) {
var10000 = "OK";
return var10000;
}
}
var10000 = "BAD";
}
return var10000;
}

函数和属性扩展

函数扩展

1
2
3
4
5
fun String.title(): String {
return if (length < 1) this else substring(0, 1).toUpperCase() + substring(1, length)
}

println("abc".title()) //Abc

反编译的Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static final String title(@NotNull String $receiver) {   
String var10000;
if ($receiver.length() < 1) {
var10000 = $receiver;
} else {
StringBuilder var7 = new StringBuilder();
byte var2 = 0;
byte var3 = 1;
StringBuilder var4 = var7;
var10000 = $receiver.substring(var2, var3);
String var5 = var10000;
if (var5 == null) {
throw new TypeCastException("null cannot be cast to non-null type java.lang.String");
}
var10000 = var5.toUpperCase();
var5 = var10000;
var7 = var4.append(var5);
var2 = 1;
int var6 = $receiver.length();
var4 = var7;
var10000 = $receiver.substring(var2, var6);
var5 = var10000;
var10000 = var4.append(var5).toString();
}
return var10000;
}

可以看到,通过在当前类里实现一个静态方法,接受被扩展类的实例对象,然后构造一个新的对象,执行扩展的操作。

属性扩展

1
2
3
4
5
6
7
8
val String.title: String
get() {
return if (length < 1) this else this.substring(0, 1).toUpperCase() + substring(1, length)
}

println("abc".title) //Abc
//or
println("abc".getTitle()) //Abc

给 String 增加一个 title属性,在 get 里实现扩展的逻辑

懒初始化 by lazy, 延迟初始化 lateinit

  • by lazy 首次使用的时候才会执行
  • lateinit 推迟一个变量的初始化
  • lazy 方法默认会添加同步锁,保证线程安全,在你确保是单线程访问的情况下, 使用 LazyThreadSafetyMode.NONE 可以避免同步的开销 。可以传递一个 LazyMode 手动指定模式
1
2
3
4
5
6
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
1
2
3
4
5
lateinit var a: String

val b: String by lazy {
"abc"
}
  • by lazy 只能用于 val 不可变对象
  • lateinit 只能用于 var 可变对象
  • lateinit 不能用于原始类型 Int Float Short Byte Char 这些

数据类

使用 data 声明

1
data class User(val name: String, val age: Int)

使用 data 关键字声明一个类是数据类,会自动生成模板代码。

注:当构造函数里有 Array 等数组类类型时,需要单独实现 equalshashCode 方法

  • getXXX
  • setXXX (如果参数定义是可变的 var )
  • copy
  • toString
  • equals
  • hashCode

自动生成的数据类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public final class User {
@NotNull
private final String name;
private final int age;

@NotNull
public final String getName() {
return this.name;
}

public final int getAge() {
return this.age;
}

public User(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = age;
}

@NotNull
public final String component1() {
return this.name;
}

public final int component2() {
return this.age;
}

@NotNull
public final User copy(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
return new User(name, age);
}

public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}

public int hashCode() {
return (this.name != null ? this.name.hashCode() : 0) * 31 + this.age;
}

public boolean equals(Object var1) {
if (this != var1) {
if (var1 instanceof User) {
User var2 = (User)var1;
if (Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
return true;
}
}
return false;
} else {
return true;
}
}

不加 data 声明

不加 data,只声明 class,会自动为字段生成 gettersetter 方法。

1
class User(val name: String, var age: Int?)

自动生成的Java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public final class User {
@NotNull
private final String name;
@Nullable
private Integer age;

@NotNull
public final String getName() {
return this.name;
}

@Nullable
public final Integer getAge() {
return this.age;
}

public final void setAge(@Nullable Integer var1) {
this.age = var1;
}

public User(@NotNull String name, @Nullable Integer age) {
Intrinsics.checkParameterIsNotNull(name, "name");
super();
this.name = name;
this.age = age;
}
}

Null 安全

Kotlin 默认定义的对象都是不能为空的,可以使用 ? 定义可空对象。如果将 null 赋给一个不能为空的对象,会触发编译期异常,从而避免了运行时 NullPointerException;如果调用一个可空对象的方法,必需增加非空判断或者使用 ?.!!. 运算符,否则会触发编译器异常。

  • ?. 如果对象为空,将不会执行后续操作
  • !!. 如果对象为空,将会抛出 NullPointerException
1
2
3
4
5
6
7
val a: String? = null

fun t() {
a.substring(0) //编译器异常: Only safe (?.) or non-null asserted (!!.) calls
a?.substring(0)
a!!.substring(0)
}


Android      Kotlin

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!