所有前端应该去学Kotlin

变量

kotlin中,val代表不可变量,var代表变量
// kotlin
var i: Int = 10; // mutable
i += 10; // 20
val j: Int = 20; // immutable
j += 20; // Error!
// typescript
// @ts-nocheck
let i: number = 10; // mutable
i += 10; // 20
const j: number = 20; // immutable
j += 30; // Error!
类型推导:kotlin中类型和typescript一样可进行类型推导
// kotlin
val i = "foo"; // String
val j = 20; // Int
val j = 20.; // Double
// typescript
let i = "foo"; // String
let j = 20; // Number

字符串模板

description: "与JS一致,当只有变量时,可省略大括号",
// kotlin
var a = 1
var s1 = "a is $a" 
a = 2
var s2 = "\${s1.replace("is", "was")}, but now is $a"
// typescript
// @ts-nocheck

流程控制

if在kotlin中可以当做三目运算符使用。if语法块的最后一行是if表达式的返回值
// kotlin
val a = 1;
val b = 2;
val max = if (a > b) a else b

// 或
val i = 1
val j = if (i > 1) {
  -1
} else {
  1
}
// typescript
// @ts-nocheck
let i = 1,
  j;
if (i > 1) {
  j = i;
} else {
  j = -i;
}
// 或
let i = 1;
let j = i > 1 ? i : -i;

模式匹配

<div> <p> Kotlin有优秀的模式匹配特性,JS提案仅在Stage 1阶段,详情请见: <a href="https://github.com/tc39/proposal-pattern-matching"> <b>这里</b> </a> </p> <p> Typescript有第三方库支持: <a href="https://github.com/tc39/proposal-pattern-matching"> <b>这里</b> </a> </p> </div>
// kotlin
when (x) {
  1 -> print("x == 1")
  2 -> print("x == 2")
  else -> {
    print("x is neither 1 nor 2")
  }
}
// typescript
// @ts-nocheck

函数

基本使用
// kotlin
// 一样可以设定默认参数
fun add(x: Int, y: Int = 0): Int { 
  return x + y;
}

//result被类型推导为Int
val result = add(3, 2) 

// 自动推导返回值类型
fun minus(x: Int, y: Int) {
  return x - y; // Int
}

// 无返回值用Unit
fun printStr(x: String): Unit{
  println(x)
}

// kotlin的简写相似于箭头函数
fun double(x: Int) = x * 2
// typescript
// @ts-nocheck
function add(x: number, y: number = 0): number {
  return x + y;
}

// result被类型推导为number
const result = add(3, 2);

// 自动推导返回值类型,无需指定
function minus(x: number, y: number = 0) {
  return x - y; // number
}

// 无返回值用void
function printStr(x: string): void {
  console.log(x);
}

// 箭头函数
const double = (x: number) => x * 2;
<p>可以将参数包装成vararg。类似于ES6的参数扩展运算符;</p> <p>当参数字段太多时,也可以使用具名参数。</p>
// kotlin
fun <T> asList(vararg ts: T): List<T> = ts.toList()

println(asList(1,2,3)) // List[1,2,3]

fun printUser(name: String, age: Int): Unit {
  println(\`My name is $name, I am $age years old. \`)
}

// 具名函数调用,无关传递顺序
println(printUser(
  age = 19,
  name = "John",
))

// typescript
// @ts-nocheck
const asList = (...arg) => {
  console.log(arg); // [1,2,3]
};

asList(1, 2, 3);

//ts没有具名函数,通过对象传递。
type User = {
  name: string;
  age: number;
};

function printUser({ name, age }: User): void {
  console.log(`My name is ${name}, I'm ${age} years old.`);
}
const user: User = {
  name: "John",
  age: 19,
};

printUser(user);
Kotlin和JavaScript一样,也函数支持局部函数
// kotlin
fun foo (a: Int) {
  val a = 100;
  fun bar() {
    println(a);
  }
  bar();
}
// typescript
// @ts-nocheck
function foo(a: number) {
  let a = 100;
  function bar() {
    console.log(a);
  }
  bar();
}
lambda表达式,类似于ES6的箭头函数。
// kotlin
// 基本使用: val 表达式名: 函数类型签名 = Lambda表达式
val add1: (Int) -> Int = {
  a: Int -> a + 1
}

// 因为有类型推断,所以可省略函数类型签名
val add2 = {
  a: Int -> a + 2
}

// 如果lambda表达式是方法最后一个参数,可以将表达式放到括号外
val product = listOf(1,2,3).fold(5) { acc, e -> acc + e }
println(product) // 11

// 如果方法只有lambda一个参数,可以省略所有括号
val doubleList = listOf(1,2,3).map { a: Int -> a * 2 }
println(doubleList) // [2,4,6]

// 如果lambda只有一个参数,可以用it代替参数,也可以省略参数it直接使用。
val tripleTripleList = listOf(1,2,3).map {it -> it * 3 }.map { it * 3 }
println(tripleTripleList) // [9,18,27]

// 如果lambda参数未用到,可以用下划线忽略。
map.forEach { (_, value) -> println("$value!") }

// typescript
// @ts-nocheck
function foo(a: number) {
  let a = 100;
  function bar() {
    console.log(a);
  }
  bar();
}

基本使用: 由于Kotlin可以自动生成get set函数, Kotlin中的class语法相较于Tyepscript非常简洁。
// kotlin
// kotlin的主要构造函数是Studen接受的参数。
// 使用val或var声明成员变量,val具有readonly的属性,自动生成get接口,var自动生成get和set接口

class Student (
  id: Int, // (1) 基本传值
  val age: Int, // (2): 当 (1)传入的参数 和 (3)成员属性名称一致时可以简化成(2)
  val isMale: Boolean = true // 默认值
  ) {

  // 初始化函数,可以做一些其他事情。

  init {
    if (id > 10000) {
      throw Exception("id不能大于10000")
    }
  }
  
  var id = id // (3): (1)参数传给成员属性。使用val或var
    set(value: Int) {
      if (value > 10000) {
        throw Exception("id不能大于10000")
      }
      field = value // field是id的值;不能写成 id = value,否则会导致setter无限递归。
    }


  val info: String
    get() = "学生的基本信息:ID - $id, 年龄 - $age ,性别 - ${if(isMale) "男" else "女"}"

  
  // 第二构造函数,用以不同的调用签名,可以有多个不同签名的第二构造函数。
  // 一般只需主要构造函数即可。
  constructor (suffixId: Int): this(id = 1000, age = 20, isMale = false) {
    id = id + suffixId
  }

  // 初始化函数可以有多个
  init {
    println(info)
    if (isMale) println("分到男生宿舍") else println("去女生宿舍吧")
  }

}

val john = Student(id = 100, age = 20, isMale = true) // 无需new关键字
println("我的ID是:${john.id}")

val aine = Student(suffixId = 1) // 另一个函数签名
println("我的年龄是:${aine.age}")
// typescript
class Student {
  private _id: number;
  private readonly _age: number;
  private readonly _isMale: boolean;

  constructor(id: number, isMale: boolean = true, age?: number) {
    if (age === undefined || isMale === undefined) {
      this.id = 1000;
      this._age = 19;
      this._isMale = false;
    } else {
      this.id = id;
      this._age = age;
      this._isMale = isMale;
    }
    this.init();
  }

  get info() {
    return `学生的基本信息:ID - ${this.id}, 年龄 - ${this.age} ,性别 - ${
      this.isMale ? "男" : "女"
    }`;
  }

  get isMale() {
    return this._isMale;
  }

  get age() {
    return this._age;
  }

  get id() {
    return this._id;
  }

  set id(value: number) {
    if (value > 10000) {
      throw new Error("id不能大于10000");
    }
    this._id = value;
  }

  private init() {
    console.log(this.info);
    if (this.isMale) console.log("分到男生宿舍");
    else console.log("去女生宿舍吧");
  }
}