本文共 17916 字,大约阅读时间需要 59 分钟。
正式上架:《Kotlin极简教程》Official on shelves: Kotlin Programming minimalist tutorial
京东JD: 天猫Tmall:
包的声明应处于源文件顶部:
package my.demoimport java.util.*// ……
目录与包的结构无需匹配:源代码可以在文件系统的任意位置。
完整的 Kotlin 方法定义语法为
[访问控制符] fun 方法名(参数列表) [:返回值类型] {}
Kotlin 可以省略变量定义的类型声明,但是在定义参数列表和定义返回值类型时则必须明确指定类型(这个类型推断Kotlin居然没做,这地方用起来比Scala,Groovy要繁琐点)。
例如,定义一个带有两个 Int
参数、返回 Int
的函数:
fun sum(a: Int, b: Int): Int { // kotlin中的返回值类型必须明确指定 return a + b}fun main(args: Array) { print("sum of 3 and 5 is ") println(sum(3, 5))}
将表达式作为函数体、返回值类型自动推断的函数:
fun sum(a: Int, b: Int) = a + bfun main(args: Array) { println("sum of 19 and 23 is ${sum(19, 23)}")}
函数返回无意义的值:
fun printSum(a: Int, b: Int): Unit { println("sum of $a and $b is ${a + b}")}fun main(args: Array) { printSum(-1, 8)}
Unit
返回类型可以省略:
fun printSum(a: Int, b: Int) { println("sum of $a and $b is ${a + b}")}fun main(args: Array) { printSum(-1, 8)}
同 Java 的变长参数一样,Kotlin 也支持变长参数
//在Java中,我们这么表示一个变长函数public boolean hasEmpty(String... strArray){ for (String str : strArray){ if ("".equals(str) || str == null) return true; } return false;}
//在Kotlin中,使用关键字vararg
来表示
fun hasEmpty(vararg strArray: String?): Boolean{ for (str in strArray){ if ("".equals(str) || str == null) return true } return false}
使用val
定义一次赋值(只读)的局部变量:
fun main(args: Array) { val a: Int = 1 // 立即赋值 val b = 2 // 自动推断出 `Int` 类型 val c: Int // 如果没有初始值类型不能省略 c = 3 // 明确赋值 println("a = $a, b = $b, c = $c")}
使用var
定义可变变量:
fun main(args: Array) { var x = 5 // 自动推断出 `Int` 类型 x += 1 println("x = $x")}
正如 Java 和 JavaScript,Kotlin 支持行注释及块注释。
// 这是一个行注释/* 这是一个多行的 块注释。 */
与 Java 不同的是,Kotlin 的块注释可以嵌套。就是说,你可以这样注释:
/** * hhhh * /** * fff * /** * ggggg * */ * */ * * abc * */fun main(args:Array){ val f = Functions() println(f.fvoid1()) println(f.fvoid2()) println(f.sum1(1,1)) println(f.sum2(1,1))}
Kotlin 程序的入口是名为"main"的函数。命令行参数通过这个方法的数组参数传递。
代码示例:
fun main(args: Array) {}
Kotlin声明变量可以用var
或者 val
。
val
声明的参数不能重新赋值(只读),但用var
声明的可以(可写)。 代码示例:
val fooVal = 10 // 不能再赋别的值给fooVal //fooVal = 11 // Val cannot be reassigned var fooVar = 10 fooVar = 20 // fooVar可以重新赋值
其中,var fooVar = 10
变量声明我们并没有指定变量的类型。
大部分情况下,Kotlin 可以判断变量的类型,所以不用每次都显式声明。
我们也可以像下面这样显式声明一个变量的类型:val foo: Int = 7
is
运算符进行类型检测is
运算符检测一个表达式是否某类型的一个实例。
如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接当作该类型使用,无需显式转换:
fun getStringLength(obj: Any): Int? { if (obj is String) { // `obj` 在该条件分支内自动转换成 `String` return obj.length } // 在离开类型检测分支后,`obj` 仍然是 `Any` 类型 return null}fun main(args: Array) { fun printLength(obj: Any) { println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ") } printLength("Incomprehensibilities") printLength(1000) printLength(listOf(Any()))}
或者
fun getStringLength(obj: Any): Int? { if (obj !is String) return null // `obj` 在这一分支自动转换为 `String` return obj.length}fun main(args: Array) { fun printLength(obj: Any) { println("'$obj' string length is ${getStringLength(obj) ?: "... err, not a string"} ") } printLength("Incomprehensibilities") printLength(1000) printLength(listOf(Any()))}
或者
fun getStringLength(obj: Any): Int? { // `obj` 在 `&&` 右边自动转换成 `String` 类型 if (obj is String && obj.length >= 0) { return obj.length } return null}fun main(args: Array) { fun printLength(obj: Any) { println("'$obj' string length is ${getStringLength(obj) ?: "... err, is empty or not a string at all"} ") } printLength("Incomprehensibilities") printLength("") printLength(1000)}
原始字符串(raw string)由三重引号(""")分隔(这个跟python一样)。原始字符串可以包含换行符和任何其他字符。
val fooRawString = """fun helloWorld(val name : String) { println("Hello, world!")}""" println(fooRawString)
字符串可以包含模板表达式。模板表达式以美元符号($)开始。
val fooTemplateString = "$fooString has ${fooString.length} characters" println(fooTemplateString)
/** * `if` is an expression, i.e. it returns a value. * Therefore there is no ternary operator (condition ? then : else), * because ordinary `if` works fine in this role. * See http://kotlinlang.org/docs/reference/control-flow.html#if-expression */fun main(args: Array) { println(max(args[0].toInt(), args[1].toInt()))}fun max(a: Int, b: Int) = if (a > b) a else b
另外,在Kotlin中没有类似true? 1: 0
这样的三元表达式。对应的写法是使用if else
语句:
if(true) 1 else 0
when表达式类似于Java中的switch。 代码示例:
fun cases(obj: Any) { when (obj) { 1 -> print("第一项") "hello" -> print("这个是字符串hello") is Long -> print("这是一个Long类型数据") !is String -> print("这不是String类型的数据") else -> print("else类似于Java中的default") }}
fun Any?.toString(): String
要使变量保持为null,必须将其显式指定为可空: 在变量类型后面加上?
符号,即声明为可空。
用 ?.
运算符来访问一个可空的变量。
?:
运算符来指定当该变量为空时的替代值 代码示例:
package com.easy.kotlin/** * Created by jack on 2017/5/30. */fun main(args: Array) { val s: String? = null println(s?.length) var fooNullable: String? = "abc" println(fooNullable?.length) // => 3 println(fooNullable?.length ?: -1) // => 3 fooNullable = null println(fooNullable?.length) // => null println(fooNullable?.length ?: -1) // => -1 testNullSafeOperator(null) testNullSafeOperator("12345678901") testNullSafeOperator("123")}fun testNullSafeOperator(string: String?) { println(string?.toCharArray()?.getOrNull(10)?.hashCode())}fun toString(any:Any): String{ return any?.toString()}
这个null check是怎样实现的呢?
我们来看一下Kotlin ByteCode。打开IDEA->Tool->Kotlin->Show Kotlin ByteCode:
我们可以看到上面的Kotlin源码对应的字节码如下:
// ================com/easy/kotlin/NullCheckKt.class =================// class version 50.0 (50)// access flags 0x31public final class com/easy/kotlin/NullCheckKt { // access flags 0x19 public final static main([Ljava/lang/String;)V @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 ACONST_NULL CHECKCAST java/lang/String ASTORE 1 L0 ALOAD 1 ASTORE 2 L1 ALOAD 2 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L2 ACONST_NULL CHECKCAST java/lang/Integer GOTO L3 L2 ALOAD 2 INVOKEVIRTUAL java/lang/String.length ()I INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; L3 INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V LDC "abc" ASTORE 2 L4 ALOAD 2 ASTORE 3 L5 ALOAD 3 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L6 ACONST_NULL CHECKCAST java/lang/Integer GOTO L7 L6 ALOAD 3 INVOKEVIRTUAL java/lang/String.length ()I INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; L7 INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V ALOAD 2 ASTORE 4 L8 ALOAD 4 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L9 ACONST_NULL CHECKCAST java/lang/Integer GOTO L10 L9 ALOAD 4 INVOKEVIRTUAL java/lang/String.length ()I INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; L10 ASTORE 3 L11 ALOAD 3 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L12 ICONST_M1 GOTO L13 L12 ALOAD 3 INVOKEVIRTUAL java/lang/Number.intValue ()I L13 INVOKESTATIC kotlin/io/ConsoleKt.println (I)V ACONST_NULL CHECKCAST java/lang/String ASTORE 2 ALOAD 2 ASTORE 3 L14 ALOAD 3 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L15 ACONST_NULL CHECKCAST java/lang/Integer GOTO L16 L15 ALOAD 3 INVOKEVIRTUAL java/lang/String.length ()I INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; L16 INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V ALOAD 2 ASTORE 4 L17 ALOAD 4 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L18 ACONST_NULL CHECKCAST java/lang/Integer GOTO L19 L18 ALOAD 4 INVOKEVIRTUAL java/lang/String.length ()I INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; L19 ASTORE 3 L20 ALOAD 3 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L21 ICONST_M1 GOTO L22 L21 ALOAD 3 INVOKEVIRTUAL java/lang/Number.intValue ()I L22 INVOKESTATIC kotlin/io/ConsoleKt.println (I)V ACONST_NULL CHECKCAST java/lang/String INVOKESTATIC com/easy/kotlin/NullCheckKt.testNullSafeOperator (Ljava/lang/String;)V LDC "12345678901" INVOKESTATIC com/easy/kotlin/NullCheckKt.testNullSafeOperator (Ljava/lang/String;)V LDC "123" INVOKESTATIC com/easy/kotlin/NullCheckKt.testNullSafeOperator (Ljava/lang/String;)V RETURN L23 LOCALVARIABLE tmp0_safe_receiver Ljava/lang/String; L1 L3 2 LOCALVARIABLE tmp1_safe_receiver Ljava/lang/String; L5 L7 3 LOCALVARIABLE tmp2_safe_receiver Ljava/lang/String; L8 L10 4 LOCALVARIABLE tmp3_elvis_lhs Ljava/lang/Integer; L11 L13 3 LOCALVARIABLE tmp4_safe_receiver Ljava/lang/String; L14 L16 3 LOCALVARIABLE tmp5_safe_receiver Ljava/lang/String; L17 L19 4 LOCALVARIABLE tmp6_elvis_lhs Ljava/lang/Integer; L20 L22 3 LOCALVARIABLE s Ljava/lang/String; L0 L23 1 LOCALVARIABLE fooNullable Ljava/lang/String; L4 L23 2 MAXSTACK = 2 MAXLOCALS = 5 // access flags 0x19 public final static testNullSafeOperator(Ljava/lang/String;)V @Lorg/jetbrains/annotations/Nullable;() // invisible, parameter 0 ALOAD 0 ASTORE 3 L0 ALOAD 3 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L1 ACONST_NULL CHECKCAST [C GOTO L2 L1 ALOAD 3 INVOKESTATIC kotlin/text/StringsKt.toCharArray (Ljava/lang/String;)[C L2 ASTORE 2 L3 ALOAD 2 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L4 ACONST_NULL CHECKCAST java/lang/Character GOTO L5 L4 ALOAD 2 BIPUSH 10 INVOKESTATIC kotlin/collections/ArraysKt.getOrNull ([CI)Ljava/lang/Character; L5 ASTORE 1 L6 ALOAD 1 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L7 ACONST_NULL CHECKCAST java/lang/Integer GOTO L8 L7 ALOAD 1 INVOKEVIRTUAL java/lang/Object.hashCode ()I INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; L8 INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V RETURN L9 LOCALVARIABLE tmp0_safe_receiver Ljava/lang/String; L0 L2 3 LOCALVARIABLE tmp1_safe_receiver [C L3 L5 2 LOCALVARIABLE tmp2_safe_receiver Ljava/lang/Character; L6 L8 1 MAXSTACK = 2 MAXLOCALS = 4 // access flags 0x19 public final static toString(Ljava/lang/Object;)Ljava/lang/String; @Lorg/jetbrains/annotations/NotNull;() // invisible @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 ALOAD 0 ASTORE 1 L0 ALOAD 1 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L1 ACONST_NULL CHECKCAST java/lang/String GOTO L2 L1 ALOAD 1 INVOKEVIRTUAL java/lang/Object.toString ()Ljava/lang/String; L2 ARETURN L3 L4 LOCALVARIABLE tmp0_safe_receiver Ljava/lang/Object; L0 L2 1 MAXSTACK = 2 MAXLOCALS = 2 // access flags 0x19 public final static()V RETURN L0 MAXSTACK = 0 MAXLOCALS = 0}
我们来单独看一下fun toString()
的bytecode:
public final static toString(Ljava/lang/Object;)Ljava/lang/String; @Lorg/jetbrains/annotations/NotNull;() // invisible @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0 ALOAD 0 ASTORE 1 L0 ALOAD 1 ACONST_NULL INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z IFEQ L1 ACONST_NULL CHECKCAST java/lang/String GOTO L2...
其中, ACONST_NULL
指令是把null
压到栈顶。然后调用
INVOKESTATIC kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)Z
这里的kotlin/jvm/internal/Intrinsics.areEqual (Ljava/lang/Object;Ljava/lang/Object;)
函数代码是:
public static boolean areEqual(Object first, Object second) { return first == null ? second == null : first.equals(second); }
反编译成Java代码,如下:
@NotNull public static final String toString(@NotNull Object any) { Intrinsics.checkParameterIsNotNull(any, "any"); return any != null?any.toString():null; }
由字节码分析可见,其实所谓的空指针安全操作符, 其实就是在内部封装了判空逻辑,来确保不出现空指针。
while循环:
/** * `while` and `do..while` work as usual. * See http://kotlinlang.org/docs/reference/control-flow.html#while-loops */fun main(args: Array) { var i = 0 while (i < args.size) println(args[i++])}
for循环
/** * For loop iterates through anything that provides an iterator. * See http://kotlinlang.org/docs/reference/control-flow.html#for-loops */fun main(args: Array) { for (arg in args) println(arg) // or println() for (i in args.indices) println(args[i])}
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 main(args: Array) { println(Color.BLUE.rgb())}
enum class Color { RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET}fun getMnemonic(color: Color) = when (color) { Color.RED -> "Richard" Color.ORANGE -> "Of" Color.YELLOW -> "York" Color.GREEN -> "Gave" Color.BLUE -> "Battle" Color.INDIGO -> "In" Color.VIOLET -> "Vain" }fun main(args: Array) { println(getMnemonic(Color.BLUE))}
/** * Kotlin Standard Library provide component functions for Map.Entry */fun main(args: Array) { val map = hashMapOf () map.put("one", 1) map.put("two", 2) for ((key, value) in map) { println("key = $key, value = $value") }}
funjoinToString( collection: Collection , separator: String, prefix: String, postfix: String): String { val result = StringBuilder(prefix) for ((index, element) in collection.withIndex()) { if (index > 0) result.append(separator) result.append(element) } result.append(postfix) return result.toString()}fun main(args: Array ) { val list = listOf(1, 2, 3) println(joinToString(list, "; ", "(", ")"))}
Kotlin的集合类有可变集合和不可变集合(lists、sets、maps 等)。精确控制集合的编辑权限,有助于消除 bug 和设计良好的 API。
预先了解一个可变集合的只读视图和一个真正的不可变集合之间的区别是很重要的。
Kotlin 的 List<out T>
类型是一个提供只读操作如 size
、get
等的接口。和 Java 类似,它继承自 Collection<T>
进而继承自 Iterable<T>
。
而改变 list 的方法是由 MutableList<T>
加入的。这一模式同样适用于 Set<out T>/MutableSet<T>
及 Map<K, out V>/MutableMap<K, V>
。
我们可以看下 list 及 set 类型的基本用法:
val numbers: MutableList = mutableListOf(1, 2, 3)val readOnlyView: List = numbersprintln(numbers) // 输出 "[1, 2, 3]"numbers.add(4)println(readOnlyView) // 输出 "[1, 2, 3, 4]"readOnlyView.clear() // -> 不能编译val strings = hashSetOf("a", "b", "c", "c")assert(strings.size == 3)fun kotlin(ktFile: String): MutableList{ val result = mutableListOf () kotlinc(ktFile) val ktClass = " " + ktFile.substring(0, ktFile.indexOf(".kt")) + "Kt" println(ktClass) val kotlin = KotlinBin.KOTLIN.binPath + ktClass println(kotlin) val runtime: Runtime = Runtime.getRuntime() val process: Process = runtime.exec(kotlin) val exitValue = process.waitFor() if (exitValue != 0) { println("exit with $exitValue") result.add("exit with $exitValue") return result } process.inputStream.bufferedReader().lines().forEach { println(it) result.add(it) } return result }
我们使用
val numbers: MutableList = mutableListOf(1, 2, 3)
创建一个初始化元素1,2,3的元素类型为Int的MutableList<Int>可变List。
使用
val result = mutableListOf()
创建一个空的MutableList<String>元素类型为String的可变List。
Kotlin 没有专门的语法结构创建 list 或 set。 要用标准库的方法,如
listOf()
、 mutableListOf()
、 setOf()
、 mutableSetOf()
。 在非性能关键代码中创建 map 可以用一个简单的来完成:mapOf(a to b, c to d)
注意上面的 readOnlyView
变量(译者注:与对应可变集合变量 numbers
)指向相同的底层 list 并会随之改变。 如果一个 list 只存在只读引用,我们可以考虑该集合完全不可变。创建一个这样的集合的一个简单方式如下:
val items = listOf(1, 2, 3)
目前 listOf
方法是使用 array list 实现的,但是未来可以利用它们知道自己不能变的事实,返回更节约内存的完全不可变的集合类型。
注意这些类型是。这意味着,你可以把一个 List<Rectangle>
赋值给 List<Shape>
假定 Rectangle 继承自 Shape。对于可变集合类型这是不允许的,因为这将导致运行时故障。
有时你想给调用者返回一个集合在某个特定时间的一个快照, 一个保证不会变的:
class Controller { private val _items = mutableListOf() val items: List get() = _items.toList()}
这个 toList
扩展方法只是复制列表项,因此返回的 list 保证永远不会改变。
List 和 set 有很多有用的扩展方法值得熟悉:
val items = listOf(1, 2, 3, 4)items.first() == 1items.last() == 4items.filter { it % 2 == 0 } // 返回 [2, 4]val rwList = mutableListOf(1, 2, 3)rwList.requireNoNulls() // 返回 [1, 2, 3]if (rwList.none { it > 6 }) println("No items above 6") // 输出“No items above 6”val item = rwList.firstOrNull()
…… 以及所有你所期望的实用工具,例如 sort、zip、fold、reduce 等等。
Map 遵循同样模式。它们可以容易地实例化和访问,像这样:
val readWriteMap = hashMapOf("foo" to 1, "bar" to 2)println(readWriteMap["foo"]) // 输出“1”val snapshot: Map= HashMap(readWriteMap)
本章示例代码:
1.
转载地址:http://tyuta.baihongyu.com/