一、kotlin基础语法学习

kotlin基础语法学习

简单的入个门, 复杂的操作放在下一章节

kotlin 包

  • 和java包不同, java类必须放在对应包名的目录下, 而kotlin没有强制要求
  • kotlin的类名可以和文件名不同
  • kotlin甚至可以不用写类

一、kotlin基础语法学习_第1张图片

package base

fun funcMax(a: Int, b: Int): Int {
   return  if(a > b) a else b
}

fun main(args: Array<String>) {
   var a = 10
   var b = 20
   println(funcMax(a, b))
}

java源码如下:

public final class FuncDemo01Kt {
   public static final int funcMax(int a, int b) {
      return a > b ? a : b;
   }

   public static final void main(@NotNull String[] args) {
      int a = 10;
      int b = 20;
      int var3 = funcMax(a, b);
      System.out.println(var3);
   }
}

kotlin编译器将文件FuncDemo01加上 Kt 做成了 java 的类名, 而maxFuncDemo01Kt类的静态方法, 在 kotlin 中被称之为 顶层函数

java调用kotlin顶层函数只需要如此:

FuncDemo01Kt.funcMax(1, 2)

下面是java调用kotlin顶层函数的方式

package base.java;

import base.FuncDemo01Kt; // ★

public class JavaTestDemo01 {
    public static void main(String[] args) throws Exception {
        int a = 10;
        int b = 30;
        System.out.println(FuncDemo01Kt.funcMax(a, b)); // ★
    }
}

函数声明和定义

kotlin使用 fun 关键字定义函数:

普通函数体形式:

fun sum(a: Int, b: Int):Int {
    return a + b
}

fun关键字 + 空格 + 函数名字(参数名: 参数类型, ...): 函数返回值 {函数体}

kotlin中还有表达式函数体形式, 如下:

fun sum(a: Int, b: Int) = a + b

fun关键字 + 空格 + 函数名字(参数名: 参数类型, ...) 表达式函数体( = a + b)

// kotlin函数无返回值时使用 `Unit` 关键字, 注意这里和`Uint`做区分, 表示`unsigned int`无符号
fun sum(a: Int, b: Int):Unit {
    // 字符串模板操作关键字 `$`
    println("max = ${if(a > b) a else b}")
}

什么是表达式?

表达式是有返回值的语句

1, -1, 1 + 1, listOf(1, 2, 3) 这些都是表达式

还有

{x:Int -> x + 1} fun(x: Int) { println(x) } if(x > 1) x else 1 这三个也是表达式

表达式比语句更加安全
void isStatement(Boolean flag) {
    String a = null;
    if (flag) {
        a = "dive into kotlin";
    }
    System.out.println(a.toUpperCase());
}

这里存在一个问题. 变量 anull, 如果不经过 if 语句, 则会在下面变成 null.toUpperCase() 直接报错

如果我们使用 kotlin 的方式重新实现这种方式

fun isStatement(flag: Boolean): Unit {
   // 我们会主动接收 if 的返回值
   val a = if (flag) "dive into kotlin" else ""
   println(a.uppercase())
}

如果我们不写上 else , idea会报错

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eAA5qm0D-1655378595637)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/868486cfe76641608aed938012e3201e~tplv-k3u1fbpfcp-watermark.image?)]

表达式的目的是为了创建新值

什么是表达式函数体?

如果一个函数的整个函数体都是一个表达式时, 我们就可以称之为表达式函数体

fun max(a: Int, b: Int) = if (a > b) a else b

表达式函数体中, 可以省略 return 关键字, 加上 等号 和表达式 if (a > b) a else b, 这意味着该函数返回了 if 表达式 的 结果

可变函数参数

fun printArray(vararg arr: Int) {
    for(v in arr) {
        println(v)
    }
}

需要注意这种用法:

fun main() {
   val list = listOf<Int>(1, 2, 3, 4, 5)
   printArray(*list.toIntArray())
   //         
}

kotlin函数值对象参数不可变

kotlin值对象参数默认为 val 修饰(表面上这么认为就行)

一、kotlin基础语法学习_第2张图片

Val cannot be reassigned

如果你按住 ctrl 将鼠标放在参数 a 上面将会看到 value-parameter a: Int 字样

一、kotlin基础语法学习_第3张图片

放在变量 b 处, 将会看到 val b: Int

一、kotlin基础语法学习_第4张图片

这是kotlin值参数类型

kotlin所有函数的参数都是值参数, 都是不可修改的

但其不影响值参数当作引用地址修改在堆中对象的情况

一、kotlin基础语法学习_第5张图片

了解值参数这一概念没啥用, 所以当作 val 变量就行了

如果将值再次赋值给 val 变量:

一、kotlin基础语法学习_第6张图片

在java中将体现为:

一、kotlin基础语法学习_第7张图片

lambda和匿名函数

匿名函数

具名函数如下:

fun max(x: Int, y: Int): Int {
   return x + y
}

匿名函数需要将函数名去除

fun (x: Int, y: Int): Int {
   return x + y
}

这种形式需要变量接收, 否则将报错

所以添加一个变量来代表匿名函数:

// 下面的 f 就是函数类型变量
val f1 = fun (x: Int, y: Int): Int {
   return x + y
}

// 然后使用 f 变量调用匿名函数
val sum = f1(190, 29)

前面的 fun 不能省略, 否则会出错.

可以使用 lambda 表达式代替匿名函数

val f2 = {x: Int, y: Int -> x + y}

还可以这样用:

比较超前, 不过学过 java 的应该看得懂点

fun running(x: Int, y: Int, funType: (Int, Int) -> Int): Int {
   return funcType(x, y)
}

// 调用方法
running(10, 299, fun (x, y): Int {
   return x + y
})

// lambda表达式
running(10, 299, { x, y -> x + y })

如果在 lambda 中写上多段代码可以这样:

running(10, 299) { x, y ->
   val ret = x + y
   println("ret = $ret")
   ret
}

匿名函数是对象, 通过函数类型 new 出来的对象, 而函数类型仅仅是个类型, 类似于 Int Double Float String Byte Long等等

lambda表达式

在kotlin中凡是同时被 {} 包裹的使用 -> 分割参数和函数体的表达式都可以称之为 lambda 表达式, 例如: { x, y -> x + y }

// lambda 表达式
val x1 = { x: Int, y: Int -> if (x > y) x else y }
val x2: (Int, Int) -> Int = { x, y -> if (x > y) x else y }
// 匿名函数方式
val y1 = fun(x: Int, y: Int): Int {
    return if (x > y) x else y
}
val y2: (Int, Int) -> Int = fun(x: Int, y: Int): Int {
    return if (x > y) x else y
}

(Int, Int) -> Int 是函数类型, 他不是对象, 他是一个类型

一、kotlin基础语法学习_第8张图片

var f = (Int, Int) -> Int 错误, 类型无法赋值

// kotlin 的初始化, 使用 lambda表达式初始化 f 类型
fun f: (Int, Int) -> Int = { x, y -> x + y }

在学习的过程中, 我发现 fun sum1(x: Int, y: Int) = {x, y -> x + y}会报错

一、kotlin基础语法学习_第9张图片

报错是 “无法推断此参数的类型。 请明确指定。”, 初学者不熟悉 kotlin 语法, 在这里会看的比较懵逼

其本质问题是: {x, y -> x + y } 是一个对象, 而函数返回值却是 Int, 返回值类型不匹配

代码最终返回值类型是 (???, ???) -> Int, ??? 表示不清楚是什么类型? {x, y -> x + y } 根本就没给出 x 和 y 的类型是什么…

如果不理解, 可以把 = 修改成 return

// 这里报错, 返回值类型是: (???, ???) -> Int
fun sum0(x: Int, y: Int) {
    return {x, y -> x + y}
}

idea智能提示应该修改成:

// 现在改成这样: (Int, Int) -> Int, 这样就不会报错了
fun sum1(x: Int, y: Int): (Int, Int) -> Int {
    return {x, y -> x + y}
}

是否更加容易理解了呢?

我们还可以像下面这种方式给 lambda 对象的参数添加类型:

fun sum(x: Int, y: Int) = {a: Int, b: Int -> 
   println("a = $a, b = $b, x = $x, y = $y") // a = 10, b = 20, x = 1, y = 2
   x + y
}

修改后, 我们可以这样调用:

sum(1, 2)(10, 20)

解析: sum(1, 2) 返回一个 lambda 对象, 类型是(Int, Int) -> Int, 还可以调用lambda函数 传递参数 10 和 20: lambda函数(10, 20)

函数内联
  • 什么是函数内联?

我们学习过C语言的人应该都知道 宏定义 吧?

#define MAX xxx

他的本质功能是代码替换, 而 kotlin 函数内联就类似于这样

private inline fun running(x: Any, funType: (Any) -> String) = funType(x)

fun main(args: Array<String>) {
    val number: Double = 1.13121356456
    running(number) {
        when (it) {
            is Byte -> Byte::class.java.name
            is Char -> Char::class.java.name
            is Boolean -> Boolean::class.java.name
            is Int -> Int::class.java.name
            is Long -> Long::class.java.name
            is Float -> Float::class.java.name
            is Double -> Double::class.java.name
            is String -> String::class.java.name
            else -> throw Exception("Unknown Type")
        }
    }
}

如果不使用内联的话, 字节码翻译出的java代码是这样:

private static final String running(Object x, Function1 funType) {
   return (String)funType.invoke(x);
}

public static final void main(@NotNull String[] args) {
   double number = 1.13121356456D;
   running(number, (Function1)null.INSTANCE); // 创建了一个对象
}

如果使用内联的话, 代码是这样:

private static final String running(Object x, Function1 funType) {
   return (String)funType.invoke(x);
}

public static final void main(@NotNull String[] args) {
   double number = 1.13121356456D;
   Object x$iv = number; // 这是 running 函数的参数
   if (x$iv instanceof Integer) {
      return Integer.TYPE.getName();
   } else if (x$iv instanceof String) {
      return String.class.getName();
   } else if (x$iv instanceof Long) {
      return Long.TYPE.getName();
   } else if (x$iv instanceof Float) {
      return Float.TYPE.getName();
   } else {
      return Double.TYPE.getName();
   }
}
  • 为什么需要函数内联?

① 在未使用函数内联的情况下, kotlin的匿名函数(lambda)通常和java一样, 需要创建一个对象, 每遇到匿名函数就需要创建一个新的对象, 导致性能损耗, 但如果使用函数内联, 编译器会在任何需要匿名函数的地方, 直接拷贝函数体的源码过去, 不需要创建对象了

② 泛型的实化

lambda的递归函数无法内联, 会导致编译器无限复制粘贴函数体

函数引用

private inline fun running(x: Int, y: Int, myFunction: (Int, Int) -> Int): Int {
    println("x: $x, y: $y")
    return myFunction.invoke(x, y)
}

private fun max(x: Int, y: Int): Int = if (x > y) x else y

fun main(args: Array<String>) {
    val x = 10
    val y = 20
    println(running(x, y, ::max)) // ★
}

函数类型作为返回值

private fun running(): (Int, Int) -> String {
    val prefix: String = "计算: "
    val postfix: String = "计算结果为: "
    return {x:Int, y: Int ->
        "${prefix}${x} + $y ${postfix}${x + y}"
    }
}

fun main(args: Array<String>) {
    val function = running()
    println(function(1, 6))
}

闭包

  • 什么是闭包?

MDN对闭包的说明很到位:

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment).

闭包是一个函数与引用该函数周围作用域变量绑定在一起的组合

In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

换句话说, 闭包给了你从一个内部函数访问外部函数作用域的能力, 在 JavaScript 中, 每次创建一个函数都会在函数创建时创建闭包

除了 JavaScript 那段, 闭包概念应该算通用的

Closures - JavaScript | MDN (mozilla.org)

  • 闭包的作用

闭包给予了作用域保护, 防止作用域出现相同函数名冲突. 像c++提出的namespace一样, kotlin存在顶层函数和属性, 如果不做作用域保护, 随着项目变大, 将有一堆函数重名

private fun running(): (Int, Int) -> String {
    val prefix: String = "计算: "
    val postfix: String = "计算结果为: "
    return {x:Int, y: Int ->
        "${prefix}${x} + $y ${postfix}${x + y}"
    }
}

变量

val/var 变量名: 变量类型

valjava 中的 final 变量

这句话错了, 也没错. kotlin 的 val 强调只读性,也就是只有 getter构造器而没有 setter 构造器, 不强调是不是 final 因为 final 是 kotlin 自带的, 任何类、变量和函数不经强调默认都是 final

kotlin编译器只生成 get 函数

var 则是可读写的属性, kotlin编译器会生成 get/set 函数

val v: Int变量 v 声明时, 如果没有进行初始化, 则变量 v 需要在后续中初始化一次

// 如果是声明时, 需要确定类型
val v: Int
v = 10

var 定义的变量, 一旦初始化了类型, 下次赋值时该变量的类型不变

var v = 10
v = "hello" // 错误, v的类型已经是 Int 了, 不可能再变成 String

=====的区别

在java 中 == 表示引用比较, equals 比较的是对象内容

在 kotlin 中 == 比较的是对象的内容, === 比较的是引用

=== 是不可以进行操作符重载的

字符串格式化模板

fun main(args: Array<String>) {
   var a: Int = 99;
   var b = 11; // 类型推导 为 Int
   println("a = $a, b = $b, a + b = ${a + b}")
}

上面这段代码是kotlin字符串模板的使用方式

  1. $变量 直接输出变量的值
  2. ${表达式} 在花括号内可以写上表达式

上面那段字符串操作模板被反编译成java代码时就会变成

public final class StringDemo01Kt {
   public static final void main(@NotNull String[] args) {
      Intrinsics.checkNotNullParameter(args, "args");
      int a = 99;
      int b = 11;
      String var3 = "a = " + a + ", b = " + b + ", a + b = " + (a + b);
      System.out.println(var3);
   }
}

看亮点:
String var3 = "a = " + a + ", b = " + b + ", a + b = " + (a + b);

类和属性

这个将会在后续章节中着重讲解

在java中类被写成这样的形式:

public class Person {
    private String name;
    private int age;
    
    public final String getName() {
        return this.name;
    }
    
    public final void setName(String name) {
        this.name = name;
    }
    
    public final int getAge() {
        return this.age;
    }
    
    public final void setAge(int age) {
        this.age = age;
    }
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

使用 idea 的java 转 kotlin 代码的转换器转换成 kotlin 源码后:

class Person(var name: String, var age: Int)

看起来舒服多了

java转化成kotlin后, public 被隐藏, 在kotlin中类是默认public, 而在java中默认是 default (public default protected private)

ps: 在作为类的成员字段, 也是属性所以需要显示的使用 val/var 来确定属性的 set/get 方法class Person(var name: String, var age: Int)而函数参数不需要 val/var fun sum(a: Int, b: Int)可能是因为它不用 get/set 方法. 不过可变参数需要主动声明 vararg, 例如: ListOf(vararg arr: String)

属性

属性和字段的区别
在java中private String name成员字段需要另外加上 get/set 方法成为属性
在kotlin val/var 声明变量时, 如果使用 val 定义变量, 将被标记成只读属性, 仅生成 get 方法, 不生成set方法, 如果使用的 var 定义变量, 则生成 set/get 方法
  • 自定义属性访问器
class Rectangle(val height: Int, val width: Int) {
   val isSquare: Boolean
//    get() {
//       return height == width
//    } // 普通函数体形式
// get() {height == width} // lamdba形式
   get() = height == width // 表达式函数体
}

当遇到 kotlin 属性 var isDelete 时, kotlin会生成 isDelete 方法和 setDelete 方法

一、kotlin基础语法学习_第10张图片

kotlin中的import关键字

import关键字在kotlin中可以导入类和顶层函数(在java中叫静态函数)

在 kotlin 中写一个扩展函数(就是把this当作参数传递进去的函数)

注意 this 当作参数的话, 将会被认为是 value-parameter 类型

fun String.lasts(): Char = this[this.length - 1]

而在 kotlin中使用 扩展函数 的方法, val ch = "kotlin".lasts()

在 java 中写成这样:

// 核心代码在这里, 把 调用 lasts 对象当作参数传递进去了, 这步骤由 kotlin 虚拟机完成
public static final Char lasts(String this /* 这个 this就是调用这个方法的对象, 就是前面例子的 "kotlin" */) {
    // "kotlin".charAt("kotlin".length() - 1) 类似于这样
    return this.charAt(this.length() - 1);
}

先提出一些后面的知识点, 之后还能回头来看看

回到正题, 我们在另一个包里调用 lasts 顶层函数(静态函数), 就会在kotlin上面见到

import base.func.lastChar

前面的 base.func 是包名, 而 lastChar 是方法

如果要在另一个包的java中调用, 就变成了

import base.func.ExtensionFuncKt;

public class ExtensionDemo01 {
    public static void main(String[] args) {
        System.out.println(ExtensionFuncKt.lastChar("zhazha"));
    }
}

枚举类

enum class Color(val r: Int, val g: Int, val b: Int) {
   RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(
      0,
      0,
      255
   ),
   INDIGO(75, 0, 130), VIOLET(238, 130, 238);
   
   fun rgb() = (r * 256 + g) * 256 + b
   
   fun getWarmth() = when (this) {
      RED, ORANGE, YELLOW -> "warm"
      GREEN -> "neutral"
      BLUE, INDIGO, VIOLET -> "cold"
   }
}

需要注意, 如果枚举类中存在额外的方法或者属性定义, 需要使用 ; 进行隔离

早期的枚举没有逗号和分号, 但定义枚举值比较麻烦, 需要这样:

RED: Color(255, 0, 0) ORANGE: Color(255, 165, 0)这样相当的麻烦, 还有很多多余的调用, 比较理想的方式就像我们上面那种RED(255, 0, 0)然而带来了新的问题, 无法区分枚举值和类方法, 几经周转(加注解或者添加新关键字等等)他们没办法只能使用 ; 来区分

借助枚举类学习 when 表达式的使用方法

when 表达式

(1) 使用函数表达式体的形式

fun getWarmth() = when (this) {
   RED, ORANGE, YELLOW -> "warm"
   GREEN -> "neutral"
   BLUE, INDIGO, VIOLET -> "cold"
   else -> ""
}

(2) 函数体形式

fun getWarmth1(): String {
   when (this) {
      RED, ORANGE, YELLOW -> return "warm"
      GREEN -> return "neutral"
      BLUE, INDIGO, VIOLET -> return "cold"
      else -> ""
   }
}

when 不需要 break 他会默认 break, when 的最后还有个 else 相当于 switchdefault

when 的每个分支都有返回值, 最终 when 表达式的返回类型就是所有分支相同的返回类型, 或者公共的父类型.

when 的参数可以忽略

when {
    sunny -> library()
    else -> study()
}

左边的 sunny 必须是 Boolean 类型

所以只要满足左边表达式的返回值类型是 Boolean 都可以放到 when

智能类型转换和is表达式类型隐式转换

interface Expr {}
class Num(val value: Int): Expr
class Sum(val left: Expr, val right: Expr): Expr

fun eval(e: Expr): Int {
   if (e is Num) {
      return e.value
   }
   else if (e is Sum) {
      return eval(e.left) + eval(e.right)
   }
   throw IllegalArgumentException("Unknown expression")
}

fun main() {
   println(eval(Sum(Num(1), Sum(Num(2), Num(3)))))
}

if (e is Num) if判断返回为 true 时, 则变量 e 被智能转化成 Num 类型

我们还可以使用 when 代替 if 表达式

使用 is 智能转换类型有前提

变量必须是 val 定义的变量, 否则无法智能转换, 只能使用 as表达式显示的转换

使用 as 强制转换类型

可以使用 as 强制类型, 如果强转不成, 直接报错.

我们还可以使用 as? , 强转不成, 直接返回 null

println((sum.left as Num).value)
val n = e as Num

for循环和范围表达式

在 Java 中我们经常这样:

for(int i = 0; i < 10; i++) {
    System.out.println(i);
}

在 kotlin 中变成这样:

for (i in 1..10) {
    println(i)
}

范围表达式

遍历

上面那种情况1..10 在官网上说通过 rangeTo 函数实现, 能够使用这种方式的类型还需要实现java.lang.Conparable接口, 比如 String

"a".."z"

就会从 "a" 遍历到 "z"

for(i in 1..10) print(i) ==> 12345678910

1…10 区间表达形式, [1, 10] 表示之间的数, 包括 1 和 10

fun main() {
   val interval: IntRange = 1..10
   for (v in interval) {
      print("$v ")
   }
   
   println()
   for (v in interval.last downTo interval.first step 1) {
      print("$v ")
   }
   println()
   
   // [1, 10)
   for (v in interval.first until interval.last) {
      print("$v ")
   }
}
1 2 3 4 5 6 7 8 9 10 
10 9 8 7 6 5 4 3 2 1 
1 2 3 4 5 6 7 8 9 
步长

kotlin还提供了 step 函数定义迭代的步长

for (i in 1..10 step 2) println(i) ==> "1359"

倒序

如果需要使用倒序, 则可以使用 downTo方法来实现:

for(i in 10 downTo 1 step 2) print(i) ==> 10 8 6 4 2

半开区间

还可以使用 unitl 函数实现 半开区间

for (i in 1 until 10) { println(i) } ==> 123456789

in 检查成员是否存在

"a" in listOf("a", "b", "c") ==> true

"kot" in "abc".."xyz" ==> true

上面那个等价于下面这个

"kot" >= "abc" && "abc" <= "xyz" ==> true

in 检查成员配合感叹号: 取反结果

"a" !in listOf("a", "b", "c")

for 使用迭代器遍历
for(c in array) {
    println(c)
}
withIndex键值组遍历
for ((index, value) in array.withIndex()) {
    println("the element at $index, is $value")
}
迭代map
fun main() {
   val map = mutableMapOf(
      1 to "zhazha",
      2 to "haha",
      3 to "haha",
      Pair(4, "xixi"),
      Pair<Int, String>(5, "heihei")
   )
   
   map.forEach(fun(m: Map.Entry<Int, String>): Unit {
      println("key = ${m.key}, value = ${m.value}")
   })
   
   for ((key, value) in map) {
      println("key = $key, value = $value")
   }
   
   // java forEach??? 调用的 java Map 里面的 BiConsumer 接口
   map.forEach { key, value ->
      println("key = $key, value = $value")
   }
   
   // Kotlin forEach??? 调用的 kotlin 的 (key, value) -> {} 遍历 Map 方法
   map.forEach { (key, value) ->
      println("key = $key, value = $value")
   }
   
}
fun Set<String>.inSet(str: String) = str in this

val set = setOf<String>("1", "2", "3", "4", "a", "b", "c")

println(set.inSet("a"))

// (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
fun isLetter(ch: Char) = ch in 'a'..'z' || ch in 'A'..'Z'

// ch >= '0' && ch <= '9'
fun inNumber(ch: Char) = ch in '0'..'9'

kotlin 异常

在kotlin中, 异常可以处理也可以不处理, 也不在函数声明上, 也不用写上throws Exception, 除非需要主动抛出异常, 则需要throw new Exception()

fun main() {
   val bufferedReader = BufferedReader(InputStreamReader(System.`in`))
   val number = readNumber(bufferedReader)
   println(number)
}

fun readNumber(reader: BufferedReader): Int? = try {
   val line = reader.readLine()
   Integer.parseInt(line)
} catch (e: NumberFormatException) {
   throw e
} finally {
   reader.close()
}
ap.forEach { key, value ->
      println("key = $key, value = $value")
   }
   
   // Kotlin forEach??? 调用的 kotlin 的 (key, value) -> {} 遍历 Map 方法
   map.forEach { (key, value) ->
      println("key = $key, value = $value")
   }
   
}
fun Set<String>.inSet(str: String) = str in this

val set = setOf<String>("1", "2", "3", "4", "a", "b", "c")

println(set.inSet("a"))

// (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
fun isLetter(ch: Char) = ch in 'a'..'z' || ch in 'A'..'Z'

// ch >= '0' && ch <= '9'
fun inNumber(ch: Char) = ch in '0'..'9'

kotlin 异常

在kotlin中, 异常可以处理也可以不处理, 也不在函数声明上, 也不用写上throws Exception, 除非需要主动抛出异常, 则需要throw new Exception()

fun main() {
   val bufferedReader = BufferedReader(InputStreamReader(System.`in`))
   val number = readNumber(bufferedReader)
   println(number)
}

fun readNumber(reader: BufferedReader): Int? = try {
   val line = reader.readLine()
   Integer.parseInt(line)
} catch (e: NumberFormatException) {
   throw e
} finally {
   reader.close()
}

你可能感兴趣的:(kotlin,android,kotlin,学习,java)