面向 JS 开发人员的 Kotlin(第一部分)
好吧,但是为什么要用 Kotlin 呢?
快速语言参考(从JS到Kotlin)
接下来该怎么办?
JavaScript 是一门神奇的语言:它从最初的“玩具语言”发展成为如今最普及的编程语言——你可以在浏览器、服务器上运行它,你可以构建移动应用程序、可穿戴设备应用程序——甚至你可以用 JavaScript 为 NASA 的太空服编写程序。
不过,学习多种语言仍然有很多机会——我甚至还没提到职业机会:正如大卫·托马斯和安德鲁·亨特在他们的经典著作《程序员修炼之道》中所说:
每年至少学习一门新语言。不同的语言用不同的方式解决相同的问题。通过学习几种不同的方法,你可以拓宽思维,避免陷入思维定势。
简而言之:是的,JavaScript 很棒,但学习一门新语言是一件很强大的事情:它会影响你思考编程的方式,给你带来新的视角,并影响你解决问题的方式,最终使你成为一名更优秀的软件开发人员。
好吧,但是为什么要用 Kotlin 呢?
市面上编程语言成百上千种,为什么偏偏是 Kotlin?原因很简单:Kotlin 的受欢迎程度(以及市场需求)正在飙升;它深受用户喜爱,并吸引了大批 JavaScript 开发者。这并非我的一家之言:GitHub 的Octoverse 报告指出 Kotlin 是其平台上增长最快的语言;而Kotlin 现状报告则显示,JavaScript 开发者是 Kotlin 新开发者的第二大来源。最后, Stack Overflow 的“内部消息”报告也充分体现了人们对 Kotlin 的喜爱。
抛开那些花哨的特性不谈,Kotlin 本身就有很多优点,使其成为你下一门编程语言的绝佳选择:
- 它采用现代语法:优雅、简洁、直接——您可以专注于完成任务。
- 它明白面向对象编程和函数式编程不是竞争关系,而是正交范式,并同时采用了这两种范式(在这种情况下,它实际上与 JavaScript 有相似之处)。
- 它是 Android 开发的官方语言,拥有许多库、扩展和优化功能。
- Kotlin 是跨平台的——它的主要目标是 JVM(Java 虚拟机——用于客户端和服务器应用程序),但您可以将其编译为 JavaScript 以便在 Web 上使用,或者编译为本地二进制文件(例如 Windows、Linux、iOS 和 Mac 库)。
最后但同样重要的是,它用起来很有趣。
快速语言参考(从JS到Kotlin)
接下来的几节中,我们将对 Kotlin 语言的语法和语义进行概览,并将其与 JavaScript 进行对比。当然,新的语法和语义会带来新的模式和构建方式,但我们暂时先不讨论这些(或其他 Kotlin 细节)——学习一门新语言的最佳方法是快速上手实践,这样你就能很快掌握更多内容(也能赶上本系列的下一篇文章 =])。
变量和常量
Kotlin 变量使用var关键字声明。为了防止重新赋值,可以使用`--return`——两者的工作方式与val JavaScript 的 `--return` 和 `--return` 非常相似。letconst
Kotlin 是一种类型化语言,这意味着在某些情况下,您需要在代码中添加类型注解。不过,大多数情况下,编译器可以自动推断类型:
JavaScript
const name = "Jon Snow";
let isAlive = true;
let role; // Declared but not initialized
Kotlin
// Types can be inferred from initialized variables
val name = "Jon Snow"
var isAlive = true
// If the variable is declared but not initialized, a type annotation is required:
var role: String
字符串
单线和多线字符串
JavaScript
const house = "Stark";
const motto = `
Winter
is
comming
`;
Kotlin
val house = "Stark"
val motto = """
Winter
is
comming
"""
字符串插值
JavaScript
const action = `Attacking using a ${weapon}`;
const result = `Looks like you will ${user.getFate()}`;
Kotlin
const action = "Attacking using a $weapon"
const result = "Looks like you will ${user.getFate()}" // curly brackets are only necessary to interpolate expressions
函数
命名函数
Kotlin 中的函数使用 `fun` 关键字声明。你需要为每个参数以及函数的返回类型添加类型注解。
JavaScript
function double(num) {
return num * 2;
}
double(2); // 4
// Default values
function shout(message, postfix = "!!!") {
return `${message.toUpperCase()}${postfix}`;
}
shout("hey"); // HEY!!!
Kotlin
fun double(num:Int) {
return num * 2
}
// Default values
fun shout(message: String, postfix = "!!!"): String {
return "${message.toUpperCase()}$postfix"
}
命名参数
在 Kotlin 中,调用函数时可以为函数参数命名。当函数具有大量参数或默认参数时,这非常方便。
Kotlin
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
...
}
reformat("SomeString", normalizeCase = false, divideByCamelHumps = true)
函数表达式(Lambda)
Lambda 表达式是“函数字面量”,即未声明的函数,而是直接作为表达式传递。在 JavaScript 中,它们通常被称为“箭头函数”,而在 Kotlin 中,则被称为“lambda 表达式”。
在 Kotlin 中,lambda 表达式始终用花括号括起来,参数的完整语法声明也放在花括号内,并且可以添加可选的类型注解,表达式主体放在一个->符号之后。注意:Kotlin 中不能为 lambda 表达式指定返回类型。大多数情况下,这没有必要,因为类型可以自动推断。
JavaScript
const double = (num) => num * 2; // Single line has implicit return
const square = (num) => {
const result = num * num;
return result; // Multi line: No implicit return
}
Kotlin
val double = { num:Int -> num * 2 }
val square = { num: Int ->
val result = num * num
// The last expression in a lambda is always considered the return value:
result
}
lambda 表达式通常只有一个参数。为了方便起见,Kotlin 会对这种情况进行特殊处理,并自动将该参数声明为 it:
JavaScript
const carModels = cars.map((car) => car.model );
const oldEnough = users.filter((user) => user.age >= 21 );
Kotlin
val carModels = cars.map { it.model }
val oldEnought = users.filter { it.age >= 21 }
流量控制
如果/否则
JavaScript
if (number > 0) {
console.log("Positive number");
} else {
console.log("Negative number");
}
Kotlin
if (number > 0) {
print("Positive number")
} else {
print("Negative number")
}
与 JavaScript(以及许多其他编程语言)不同,ifKotlin 中的表达式会返回一个值:
JavaScript
let result;
if (number > 0) {
result = "Positive number";
} else {
result = "Negative number";
}
Kotlin
val result = if (number > 0) {
"Positive number"
} else {
"Negative number"
}
在 Kotlin 中,如果代码写在一行中,可以省略花括号——因此没有三元运算符:
JavaScript
const result = number > 0 ? "Positive number" : "Negative number";
Kotlin
val result = if (number > 0) "Positive number" else "Negative number"
开关(何时)
Kotlin 有一个when可以被视为 JavaScriptswitch语句替代品的结构:
JavaScript
switch (selectedFruit) {
case "orange":
console.log("Oranges are 59 cents a pound.");
break;
case "apple":
console.log("Apples are 32 cents a pound.");
break;
case "cherry":
console.log("Cherries are one dollar a pound.");
break;
case "mango":
case "papaya":
console.log("Mangoes and papayas are 3 dollars a pound.");
break;
default:
console.log(`Sorry, we are out of ${selectedFruit}.`);
}
Kotlin
when(selectedFruit) {
"orange" -> print("Oranges are 59 cents a pound.")
"apple" -> print("Apples are 32 cents a pound.")
"cherry" -> print("Cherries are one dollar a pound.")
"mango", "papaya" -> print("Mangoes and papayas are 3 dollars a pound.")
else -> print("Sorry, we are out of $selectedFruit.")
}
实际上,Kotlin 的 Switchwhen功能远比这强大得多,许多人将其形容为“拥有超能力的 Switch”。以下是它的一些when优势:
- 它可以作为一种表达方式
- 它可以包含任意条件表达式
- 它可以自动转换值。
- 它可以不带论证使用。
- 它可以用来要求穷尽所有可能的情况(所有可能的条件都需要满足)。
本文不会深入探讨 Kotlin 的语法when,但需要注意的是,它在代码编写中非常有用。如果您准备好了,可以参考这篇文章了解更多信息。
循环
Kotlin 确实提供了for循环,但它只能用于迭代器(例如 List 和 Map)。幸运的是,Kotlin 也提供了范围(range),即可以使用以下..运算符创建的迭代器:
JavaScript
for (let i = 1; i<=10; i++) {
console.log(i);
}
// 1 2 3 4 5 6 7 8 9 10
const places = ["New York", "Paris", "Rio"];
for (const place of places) {
console.log(`I Love ${place}`);
}
// I Love New York
// I Love Paris
// I Love Rio
Kotlin
for (i in 1..10) {
print(i)
}
// 1 2 3 4 5 6 7 8 9 10
val places = listOf("New York", "Paris", "Rio")
for (place in places) {
println("I Love $place")
}
// I Love New York
// I Love Paris
// I Love Rio
收藏
Kotlin 不提供集合字面量(例如数组的 [] 或对象的 {})。相反,它提供可用于创建集合的全局函数。这样做的理由是,如果语言提供集合字面量,就会影响用户使用集合的方式(进而影响用户使用语言本身的方式)。可变与不可变、列表与数组——Kotlin 赋予用户选择的自由。
数组(这里指的是“可以动态增长的内容列表……”)
“数组”是一个含义丰富的术语,在不同的编程语言中可以指代不同的东西——Kotlin 就是如此:它确实有数组,但它们与 JavaScript 数组并不相同(也就是说,它们不是可以动态增长或缩小的列表)。在这种情况下,更合适的集合是 List。
JavaScript
const houses = [ "Stark", "Lannister", "Tyrell", "Arryn", "Targaryen", "Baratheon" ];
houses[2]; // "Tyrell"
houses.push("Martell");
houses.length; //7
Kotlin
val houses = mutableListOf("Stark", "Lannister", "Tyrell", "Arryn", "Targaryen", "Martell", "Baratheon")
houses[2] // "Tyrell"
houses.add("Martell")
houses.size //7
对象(在本例中指的是“键值映射”)
JavaScript 中的对象用途广泛:它既是哈希表,也是除基本类型之外所有类型的基础构造(更不用说它还包含诸如 `Object.assign` 之类的实用方法)。既然我们正在讨论集合,我将以哈希表为例进行说明(存储键值对)。
JavaScript
const colors = {
"red": 0xff0000,
"green": 0x00ff00,
"blue": 0x0000ff,
"cyan": 0x00ffff,
"magenta": 0xff00ff,
"yellow": 0xffff00
};
colors.hasOwnProperty("yellow"); // true
colors.yellow; // 0xffff00
Kotlin
val colors = mutableMapOf(
"red" to 0xff0000,
"green" to 0x00ff00,
"blue" to 0x0000ff,
"cyan" to 0x00ffff,
"magenta" to 0xff00ff,
"yellow" to 0xffff00
)
colors.contains("yellow") // true
colors.get("yellow") // 0xffff00
关于不变性的简要说明
Kotlin 还提供了其集合的只读版本:
Kotlin
// mapOf is the read-only version of mutableMapof
val colors = mapOf(
"red" to 0xff0000,
"green" to 0x00ff00,
"blue" to 0x0000ff
)
val updatedColors = colors.plus("teal" to 0x008080) // doesn't change the original - it returns a new map
// listOf is the read-only version of mutableListof
val houses = listOf("Stark", "Lannister", "Tyrell", "Arryn", "Targaryen", "Martell", "Baratheon")
// Methods that return a new list instead of modifying it are still available:
var updatedHouses = houses.take(3).map {it.toUpperCase()} //["STARK", "LANNISTER", "TYRELL"]
// Adding new items requires copying the whole original one and making sure the new copy is also immutable
var updatedHouses = houses.toMutableList().apply{ add("Martell") }.toList()
解构任务
解构声明语法非常方便,可以节省一些代码:当你将一个集合赋值给一个值时,Kotlin 会将集合拆分并匹配,把右侧的值赋给左侧的变量。最简单的情况下,它可以用于并行赋值:
JavaScript
const coordinates = [5, 10, 15];
const [x, y, z] = coordinates;
Kotlin
val coordinates = arrayOf(5, 10, 15)
val (x, y, z) = coordinates
虽然上面的例子看起来有点傻,但这对于处理返回多个值的函数来说尤其方便:
JavaScript
function weatherReport(location) {
// Make an Ajax request to fetch the weather...
return [72, "Mostly Sunny"];
}
const [temp, forecast] = weatherReport("Berkeley, CA");
Kotlin
fun weatherReport(location) {
// Make an Ajax request to fetch the weather...
return Pair(72, "Mostly Sunny") // Pair is a standard class in Kotlin that represents a generic pair of two values
}
val (temp, forecast) = weatherReport("Berkeley, CA")
课程
与 JavaScript 类似,Kotlin 中的类也是使用关键字声明的class:
JavaScript
class Monster {
constructor(name, color, numEyes) {
this.name = name;
this.color = color;
this.numEyes = numEyes;
}
speak(likes) {
return `My name is ${this.name} and I like ${likes}`;
}
}
var nhama = new Monster("Nhama", "red", 1);
nhama.speak("guacamole")
// "My name is Nhama and I like guacamole"
Kotlin
class Monster(val name: String, val color: String, val numEyes: Int) {
fun speak(likes: String):String {
return "My name is $name and I like $likes"
}
}
var nhama = Monster("Nhama", "red", 1)
// Kotlin doesn't have a `new` keyword - you instantiate a class by calling it directly
nhama.speak("guacamole")
// "My name is Nhama and I like guacamole"
Kotlin 类也有构造函数,但如果您只想定义类属性,则可以省略该关键字。
数据容器
在 JavaScript 中,通常会创建普通对象来将命名值组合在一起。而在 Kotlin 中,你需要创建数据类——它们同样可以作为数据容器,但更轻量级,字段名称固定,并且类型更加严格。
JavaScript
const movie1 = {
name: "Back to the Future",
rating: 5,
director: "Bob Zemeckis"
}
const movie2 = {
name: "Star Wars: Episode IV - A New Hope",
rating: 5,
director: "George Lucas"
}
Kotlin
data class Movie(
val name: String,
val rating: Int,
val director: String
)
val movie1 = Movie("Back to the Future", 5, "Bob Zemeckis")
val movie2 = Movie("Star Wars: Episode IV - A New Hope", 5, "George Lucas")
接下来该怎么办?
本文始终将 Kotlin 语法与 JavaScript 语法进行对比——然而,Kotlin 在某些方面采用了独特的处理方式,与 JavaScript 截然不同——最突出的例子就是 Kotlin 的空值处理方式。在本文的第二部分,我将介绍 Kotlin 的一些独特特性:空安全以及异步编程——敬请期待。
文章来源:https://dev.to/cassiozen/kotlin-for-js-devs-part-1-5bld

