开始切换到 Kotlin_ 谷歌工程师给初学者的知识点总结

fun add(name: String, description: String): Entry

和分号是可选项类似,如果您还没有习惯使用这个特性的话,可能会让您难以接受,它与很多人在他们编程生涯中遇到过的类型声明相比,这里的顺序正好相反。

但这个语法带来的好处是,如果变量类型是可以自动被推测出来的话,此时可以直接跳过类型声明。这个特性在后面的 “省略变量类型” 章节里有介绍。

还有个好处是可以把更多的注意力放在变量本身而不是它的类型上。而且我发现在讨论代码的时候,类型在后的顺序听起来更自然 (从英文语言角度)。

/// Java
int result; // 整数型的变量,名字叫 “result”
/// Kotlin
var result: Int // 变量名字叫 “result” ,是整数型

对此语法我想说的最后一件事情是,虽然一开始您可能感觉不适,但是随着使用频率的增加,您慢慢会习惯。

没有 new 关键字的构造函数

Kotlin 中不需要使用 new 关键字调用构造函数。

/// Java
… = new SomeClass();
/// Kotlin
… = SomeClass()

起初这会让您觉得漏掉了关键信息 (指创建内存的操作),但不需要担心。因为在 Java 中,有些函数会在您不知情的情况下创建内存。对此,您从来也没有关心过 (也不需要关心)。很多函数库甚至还有创建内存的静态方法,比如:

/// Java
Lists.newArrayList();

Kotlin 只是统一了函数的这种行为。因为对一个函数来说,无论分配内存与否,这只是它的附加效果而已,并不影响函数调用。

而且它还简化了创建对象只是为了调用其方法的写法。

/// Java
new Thread(…).start(); // 有点奇怪但是是可以这样写的
/// Kotlin
Thread(…).start()

可变性与不可变性

在 Java 中变量默认是可变的,使用 final 关键字可以使变量为不可变。与之相反,在 Kotlin 中是没有 final 关键字。您需要使用 val 关键字指示变量是不可变的,使用 var 关键字指示变量是可变的。

/// Java
private final Project project; // 初始化之后无法再赋值
private Module activeModule; // 初始化之后可以再赋值
/// Kotlin
private val project: Project // 初始化之后无法再赋值
private var activeModule: Module // 初始化之后可以再赋值

在 Java 中您可能会经常遇到很多成员变量应该是常数,但是却没有使用 final 关键字 (忘记加上 final 是容易犯的错误)。在 Kotlin 中您必须显式地声明每个成员变量的类型。如果您一开始不确定该选择哪种类型,那就默认使用 val 类型,后面有需求变化时再改为 var。

顺便说一句,在 Java 中函数参数类型是可变的,但是可以使用 final 关键字修改为不可变。在 Kotlin 中,函数参数始终是不可变的,它们是被 val 关键字隐式地标记为不可变。

/// Java
public void log(final String message) { … }
/// Kotlin
fun log(message: String) { … } // “message” is immutable

可空性

Kotlin 中取消了 @NotNull 跟 @Nullable 的注解方法。如果变量可以赋值为 null,您只需要在变量类型后面加上一个 “?” 就可以了:

/// Java
@Nullable Project project;
@NotNull String title;
/// Kotlin
val project: Project?
val title: String

在某些情况下,当您确定某些可以被赋值为 null 的变量不可能是 null,您可以使用 !! 操作符设置一个断言。

/// Kotlin
// ‘parse’ 可以返回 null,但这条用例总是能够运行
val result = parse(“123”)!!
// 下面这行是多余的,因为 !! 已经触发断言了
❌ assertThat(result).isNotNull()

如果您错误地使用了 !!,它有可能会抛出 NullPointerException 的异常。在单元测试中,这只会造成测试用例的失败,但是在生产环境中,可能会使程序崩溃,所以要非常小心。事实上,在生产环境的代码中有太多的 !! 操作符,可能意味着此处有 “代码异味” (“代码异味” 代表这部分代码可能需要审查或重构)。

在单元测试中,测试用例里使用 !! 操作符是可接受的,原因是当假设不成立的时候测试用例会失败,并且您还可以修复它。

如果您确定使用 !! 操作符是有意义的,那尽量靠前使用,如下面的用法:

/// Kotlin
val result = parse(“…”)!!
result.doSomething()
result.doSomethingElse()

下面是错误用法:

/// Kotlin (自动生成)
val result = parse(“…”)
❌ result!!.doSomething()
❌ result!!.doSomethingElse()

可省略变量的类型

在 Java 中会看到如下写法的代码:

/// Java
SomeClass instance1 = new SomeClass();
SomeGeneric<List> instance2 = new SomeGeneric<>();

在 Kotlin 中,类型声明被认为是冗余的操作,不需要写两次:

/// Kotlin
val instance1 = SomeClass()
val instance2 = SomeGeneric<List>()

然而,我们在使用动态绑定的时候可能会需要声明这些类型:

/// Java
BaseClass instance = new ChildClass(); // 如:List = new ArrayList

在 Kotlin 中使用下面语法达到同样目的:

/// Kotlin
val instance: BaseClass = ChildClass()

没有检查性异常

不像 Java 那样,Kotlin 中的类方法不需要声明自己的异常类型。因为在 Kotlin 中检查性异常 (Checked Exception) 跟运行时异常 (Runtime Exception) 之间是没有区别的。

/// Java
public void readFile() throws IOException { … }
/// Kotlin
fun readFile() { … }

但是为了使 Java 可以调用 Kotlin 的代码,Kotlin 还是提供 @Throws 注解功能,用于隐式的声明异常类型。当执行 Java → Kotlin 转换时,IDE 会保证安全性并且始终包含这类信息。

/// Kotlin (从 Java 自动转换而来)
@Throws(Exception::class)

fun testSomethingImportant() { … }

但您不需要担心您的单元测试会被 Java 类调用。因此,您可以安全地删除这些类型声明而且还可以减少代码行数:

/// Kotlin
fun testSomethingImportant() { … }

Lambda 调用时可以省略括号

在 Kotlin 中,如果您想要把一个闭包赋值给变量,您需要显式地声明它的类型:

val sumFunc: (Int, Int) -> Int = { x, y -> x + y }

如果所有类型是可以被推测的,则可以简写成:

{ x, y -> x + y }

举个例子:

val intList = listOf(1, 2, 3, 4, 5, 6)
val sum = intList.fold(0, { x, y -> x + y })

需要注意的是,如果一个函数调用的最后一个参数是 lambda 调用时,这时候可以把闭包写在函数括号的外面。

上面代码等同于如下:

val sum = intList.fold(0) { x, y -> x + y }

有能力做,但并不意味着您应该这么做。有些人会觉得上面使用 fold 的方法比较奇怪。某些场景下这种语法减少了视觉干扰,特别是函数的参数只有一个闭包时。如果我们想统计偶数的数量时,对比如下两个用法:

用法一:

intList.filter({ x -> x % 2 == 0 }).count()

用法二:

intList.filter { x -> x % 2 == 0 }.count()

或者对比 Thread 函数使用,如下两个用法:

用法一:

Thread({ doThreadWork() })

用法二:

Thread { doThreadWork() }

无论您喜欢与否,当您在 Kotlin 中看到这类用法时您应该知道它是怎么工作的,Java → Kotlin 转换中也会用到这种语法。

equals() 方法、 == 与 === 运算符

Kotlin 在相等性测试方面不同于 Java。

在 Java 中,== 运算符是用于比较两个对象的引用是否相同,它是有别于 equals() 方法。尽管从理论上听起来不错,在实践中开发者经常会在需要使用 equals 的地方使用了 == 运算符。这可能会引入不易察觉的 Bug,需要花费数小时来定位问题。

在 Kotlin 中 == 运算符等同于 equals 方法,唯一的区别是它还能正确地处理与 null 间的比较。举个例子,null==x 是合法的操作,但是 null.equals(x) 会抛出 NullPointerException 异常。

如果您想在 Kotlin 中判断对象引用的相等性,那您可以使用 === 运算符,这种语法不容易用错而且还更容易定位问题。

/// Java
Color first = new Color(255, 0, 255);
Color second = new Color(255, 0, 255);
assertThat(first.equals(second)).isTrue();
assertThat(first == second).isFalse();
/// Kotlin
val first = Color(255, 0, 255)
val second = Color(255, 0, 255)
assertThat(first.equals(second)).isTrue()
assertThat(first == second).isTrue()
assertThat(first === second).isFalse()

当您使用 Kotlin 的时候,大部分情况下会使用 == 运算符,因为 === 运算符的应用场景相对来说比较少。需要指出的是,Java → Kotlin 转换器始终会把 Java 中的 == 运算符转换成 Kotlin 中的 === 运算符。出于代码可读性跟功能意图考虑,在必要时您应该把 === 运算符恢复成 == 运算符。对比枚举类型时经常会遇到上面所说的情况,如下所示:

/// Java
if (day == DayOfWeek.MONDAY) { … }

/// Kotlin (从 Java 自动转换而来)
❌ if (day === DayOfWeek.MONDAY) { … }

/// Kotlin
if (day == DayOfWeek.MONDAY) { … }

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
15905047875)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值