├── .gitignore ├── README.md ├── index.html ├── kotlin-tutorial-basic.md ├── notes.md ├── notes.pdf ├── remark-latest.min.js ├── slides.md └── src └── com └── mcxiaoke └── kotlin ├── Basics.kt ├── ClassesAndInheritance.kt ├── DataClasses.kt ├── Delegation.kt ├── Extensions.kt ├── FunctionsAndLambdas.kt ├── Generics.kt ├── GettingStarted.kt ├── InterfacesAndVisibilityModifiers.kt ├── Interoperability.kt ├── NestedClassesAndEnum.kt ├── Objects.kt ├── Others.kt ├── PropertiesAndFields.kt ├── builders └── TypeSafeBuilders.kt └── stdlib └── Core.kt /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | out/ 3 | build/ 4 | classes/ 5 | *.iml 6 | .gradle/ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlin中文教程 2 | 3 | * [Kotlin入门和使用讲稿(PPT)](slides.md) 4 | * [Kotlin入门和使用讲稿(文本)](notes.md) 5 | * [我的学习笔记: Kotlin教程(入门篇)](kotlin-tutorial-basic.md) 6 | * [Kotlin: Java 6 废土中的一线希望](https://realm.io/cn/news/droidcon-michael-pardo-kotlin/) 7 | * [Kotlin: 高级Android开发](https://realm.io/cn/news/oredev-jake-wharton-kotlin-advancing-android-dev/) 8 | * [为什么说Kotlin值得一试](http://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg==&mid=404087761&idx=1&sn=d80625ee52f860a7a2ed4c238d2151b6) 9 | * [Kotlin中文站](http://tanfujun.com/kotlin-web-site-cn/) 10 | * [Kotlin官方文档翻译系列(简书)](http://www.jianshu.com/notebooks/2822252/latest) 11 | * [给Android开发者的Kotlin教程](https://github.com/wangjiegulu/kotlin-for-android-developers-zh/blob/master/SUMMARY.md) 12 | 13 | # Awesome-Kotlin 14 | 15 | * [https://github.com/mcxiaoke/awesome-kotlin](https://github.com/mcxiaoke/awesome-kotlin) 16 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Kotlin 5 | 6 | 80 | 81 | 82 | 84 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /kotlin-tutorial-basic.md: -------------------------------------------------------------------------------- 1 | # Kotlin教程:入门篇 2 | 3 | ## Kotlin介绍 4 | 5 | 官网: 6 | 7 | Kotlin是JetBrains在JVM上推出的新一代静态类型编程语言,具备泛型,高阶函数,Lambda,闭包,代理属性等大多数现代编程语言的特性,支持可空类型,语法简洁。Kotlin生来就是为了弥补Java缺失的现代语言的特性,并极大的简化了代码,使得开发者可以编写尽量少的代码,非常适合于Android开发,推荐所有Android开发者学习。 8 | 9 | ### Kotlin安装 10 | 11 | 可以下载单独的Kotlin编译工具,建议直接下载 IntelliJ IDEA 15,然后安装Kotlin插件,创建新项目的时候选择Kotlin支持即可 12 | 13 | 单独安装Kotlin的方法,Unix系列的系统可以使用SDKMAN安装,方法如下: 14 | ```shell 15 | curl -s get.sdkman.io | bash 16 | sdk install kotlin 17 | ``` 18 | 19 | OS X系统可以使用Homebrew安装,如下: 20 | 21 | ```shell 22 | brew update 23 | brew install kotlin 24 | ``` 25 | 26 | Kotlin版的Hello, World代码如下: 27 | 28 | ```kotlin 29 | fun main(args: Array) { 30 | println("Hello, World!") 31 | } 32 | ``` 33 | 如果使用IDEA,点击编辑器窗口main函数左边的标记可以运行代码,命令行需要这样做: 34 | 35 | ```shell 36 | kotlinc hello.kt -include-runtime -d hello.jar 37 | java -jar hello.jar 38 | ``` 39 | 40 | ## 基本语法 41 | 42 | 下面开始介绍Kotlin的基本语法 43 | 44 | ### 定义包 45 | 46 | Kotlin和Java一样使用package组织代码,package定义必须放在源文件的顶部,如 47 | 48 | ```kotlin 49 | package com.mcxiaoke.koi 50 | import java.util.* 51 | ``` 52 | 53 | import语句也和Java类似 54 | 55 | ```kotlin 56 | import foo.Bar 57 | import foo.* 58 | ``` 59 | 60 | 还可以使用`as`关键字解决命名冲突 61 | 62 | ```kotlin 63 | import foo.Bar // Bar is accessible 64 | import bar.Bar as bBar // bBar stands for 'bar.Bar' 65 | ``` 66 | import还可以用于导入顶层作用域(不在任何类的内部)定义的函数和属性,可以导入对象内的函数和属性,导入enum常量 67 | 68 | 和Java不同的是,package定义不需要和磁盘上的目录结构保持一致,源文件可以放在任何地方,但是建议保持一致 69 | 70 | ### 定义函数 71 | 72 | Kotlin的main函数是这样的,和Java差不多,Kotlin的函数默认是public的 73 | ```kotlin 74 | fun main(args: Array) { 75 | println("hello, world") 76 | } 77 | ``` 78 | 79 | Kotlin的语句不需要分号做结束符,函数用`fun`关键字,个人觉得用`func`更符合习惯,或者用`function`也不错,用`fun`有点不伦不类的 80 | 81 | 下面是有两个Int参数,返回值为Int类型的函数: 82 | ```kotlin 83 | fun sum(a: Int, b: Int): Int { 84 | return a + b 85 | } 86 | ``` 87 | 88 | 下面的函数使用一个表达式作为函数体,自动推断返回类型 89 | ```kotlin 90 | fun sum(a: Int, b: Int) = a + b 91 | ``` 92 | 如果函数体足够简单,只需要一个表达式,就可以省略大括号,直接将表达式放在等于号 = 后面,就像上面的sum函数一样 93 | 94 | 从上面的例子可以看出,Kotlin的类型声明是放在变量名后面,用冒号:分隔,函数的返回类型也是放在函数声明的后面,大括号之前,也是用冒号:分隔 95 | 96 | 下面的函数的 返回类型为Unit,相当于Java里的Void,即没有返回值,Unit可以省略: 97 | 98 | ```kotlin 99 | fun printSum(a: Int, b: Int): Unit { 100 | print(a + b) 101 | } 102 | ``` 103 | 104 | 省略Unit后 105 | 106 | ```kotlin 107 | fun printSum(a: Int, b: Int) { 108 | print(a + b) 109 | } 110 | ``` 111 | 112 | ### 定义局部变量 113 | 114 | 定义一次性赋值(只读)的局部变量使用`val`关键字 115 | 116 | ```kotlin 117 | fun localVariables() { 118 | val a: Int = 1 119 | val b = 1 // 自动推导类型为Int 120 | val c: Int // 没有初始值时需要显式制定变量类型 121 | c = 1 // 初始复制 122 | // c = 2 这个是错误的,val定义的只读变量不可重新赋值 123 | } 124 | ``` 125 | 126 | `val`大致相当于Java里的final,C/C++里的const,Swift里的let,使用val定义的是不可变量,这种类型的变量有很多优点,后面会提到 127 | 128 | 定义可重复赋值(读写)的变量使用`var`关键字 129 | 130 | ```kotlin 131 | fun mutableVariables() { 132 | var x = 5 // 类型自动推导为Int 133 | x += 1 134 | } 135 | ``` 136 | 137 | `var`定义的变量就是大部分编程语言里的普通变量,可读写,可重新赋值,Swift也是使用`var` 138 | 139 | 140 | ### 字符串模板 141 | 142 | Kotlin支持许多动态语言早就支持的字符串模板,与Groovy和Swift类似 143 | 144 | ```kotlin 145 | fun strTemplate() { 146 | val name = "Miufox" 147 | val age = 6 148 | print("I have a cat, name is $name age:$age") 149 | 150 | val a:Int = 2016 151 | val b = 40 152 | print("sum($a+$b) = ${a+b}") 153 | 154 | var args = arrayOf("Cat", "Dog", "Rabbit") 155 | print("Hello ${args[0]}") 156 | } 157 | ``` 158 | 159 | 在字符串模板中可以使用变量和表达式,表达式需要使用${}包裹,简单的变量可以省略大括号 160 | 161 | ### 条件表达式 162 | 163 | Kotlin中的if语句和Java类似 164 | 165 | ```kotlin 166 | fun max(a: Int, b: Int): Int { 167 | if (a > b) 168 | return a 169 | else 170 | return b 171 | } 172 | ``` 173 | 174 | 使用if表达式,上面的函数可以简化成这样 175 | 176 | ```kotlin 177 | fun max(a: Int, b: Int) = if (a > b) a else b 178 | ``` 179 | 180 | ### 空值检查 181 | 182 | Kotlin中,如果一个值可能为null就必须显式标示为`nullable`,使用问号?,下面的函数返回可能为null 183 | 184 | ```kotlin 185 | fun parseInt(str: String): Int? { 186 | // ... 187 | } 188 | ``` 189 | 190 | 使用返回值可能为null的函数 191 | 192 | ```kotlin 193 | fun testInt(args: Array) { 194 | if (args.size < 2) { 195 | print("Two integers expected") 196 | return 197 | } 198 | val x = parseInt(args[0]) // Int? 199 | val y = parseInt(args[1])//Int? 200 | if (x != null && y != null) { 201 | // null检查之后,这里自动类型转换为非空值 202 | print(x * y) 203 | } 204 | } 205 | 206 | ``` 207 | 208 | 详细的说明可以参考空值安全章节 209 | 210 | ### 类型检查和自动转换 211 | 212 | `is`操作符用于检查某个对象是否是指定的类型,检查完成后自动转换为指定的类型,无需再显式转换 213 | 214 | ```kotlin 215 | fun getStringLength(obj: Any): Int? { 216 | if (obj is String) { 217 | // `obj` 自动转换为 `String` 218 | return obj.length 219 | } 220 | // 在类型检查的if分支外 obj依然是 `Any` 类型 221 | return null 222 | } 223 | ``` 224 | 225 | 下面的例子可能更清晰一些 226 | 227 | ```kotlin 228 | fun getStringLength(obj: Any): Int? { 229 | if (obj !is String) 230 | return null 231 | // `obj` 自动转换为 `String` 类型 232 | return obj.length 233 | } 234 | ``` 235 | 236 | 甚至可以这样写 237 | 238 | ```kotlin 239 | fun getStringLength(obj: Any): Int? { 240 | // && 右边 obj 已经自动转换为 String 类型 241 | if (obj is String && obj.length > 0) 242 | return obj.length 243 | 244 | return null 245 | } 246 | ``` 247 | 248 | ### 循环语句 249 | 250 | for循环使用in操作符,相当于Java的冒号 251 | 252 | ```kotlin 253 | fun forLoop1(args: Array) { 254 | for (arg in args) { 255 | print(arg) 256 | } 257 | } 258 | ``` 259 | 260 | 或者这样写,遍历数组,后面会看到更优雅的方法 261 | 262 | ```kotlin 263 | fun forLoop2(args: Array) { 264 | for (i in args.indices) { 265 | print(args[i]) 266 | } 267 | } 268 | ``` 269 | 270 | while循环和Java类似 271 | 272 | ```kotlin 273 | fun whileLoop1(args: Array) { 274 | var i = 0 275 | while (i < args.size) 276 | print(args[i++]) 277 | } 278 | ``` 279 | 280 | Kotlin增加的Java没有的when表达式,支持强大的类型匹配功能,这里是一个简单的例子 281 | 282 | ```kotlin 283 | fun cases(obj: Any) { 284 | when (obj) { 285 | 1 -> print("One") 286 | "Hello" -> print("Greeting") 287 | is Long -> print("Long") 288 | !is String -> print("Not a string") 289 | else -> print("Unknown") 290 | } 291 | } 292 | ``` 293 | 294 | Range表达式 295 | 296 | ```kotlin 297 | fun range1(x: Int, y: Int) { 298 | if (x in 1..y - 1) { 299 | print("OK") 300 | } 301 | } 302 | ``` 303 | 304 | 另一个例子 305 | 306 | ```kotlin 307 | fun range2(x: Int, array: Array) { 308 | if (x !in 0..array.lastIndex) { 309 | print("Out") 310 | } 311 | } 312 | 313 | fun range3(x: Int) { 314 | if (x in 1..5) { 315 | print(x) 316 | } 317 | } 318 | ``` 319 | 320 | 使用集合的简单例子,Kotlin支持Java的集合类型,但比Java强大很多 321 | 322 | ```kotlin 323 | // 集合类型 324 | fun names1(names: Array) { 325 | for (name in names) { 326 | println(name) 327 | } 328 | } 329 | 330 | // 检查是否包含 331 | fun names2(text: String, names: Array) { 332 | if (text in names) { 333 | print("Yes") 334 | } 335 | } 336 | 337 | // 使用lambda表达式 338 | fun names3(names: Collection) { 339 | names.filter { it.startsWith("A") } 340 | .sortedBy { it } 341 | .map { it.toUpperCase() } 342 | .forEach { print(it) } 343 | } 344 | ``` 345 | 346 | ## 数据类型 347 | 348 | ### 数字类型 349 | 350 | Kotlin提供下列内置类型,与Java的基本数据类型是对应的 351 | ``` 352 | Type Bit width 353 | Double 64 354 | Float 32 355 | Long 64 356 | Int 32 357 | Short 16 358 | Byte 8 359 | ``` 360 | 361 | ### 字面量 362 | 363 | 十进制: 123 364 | Long类型 以L结尾: 123L 365 | 十六进制: 0x0F 366 | 二进制: 0b00001011 367 | 注意:不支持八进制 368 | 369 | 浮点数默认是Double: 123.5, 123.5e10 370 | Float类型以F或f结尾: 123.5f 371 | 372 | ### 类型转换 373 | 374 | Int类型不是Long类型的子类型,下面的代码无法通过编译 375 | ```kotlin 376 | fun conversation1() { 377 | val a: Int? = 1 // Int 包装类型 (java.lang.Integer) 378 | // val b: Long? = a // Long 包装类型 (java.lang.Long) 379 | // print(a == b) // 结果是false,因为两者类型不一样 380 | val b: Byte = 1 // OK, literals are checked statically 381 | // val i: Int = b // ERROR 382 | val i: Int = b.toInt() // OK: 显式转换 383 | } 384 | ``` 385 | 386 | 所有的数字类型都支持下列转换 387 | 388 | ``` 389 | oByte(): Byte 390 | toShort(): Short 391 | toInt(): Int 392 | toLong(): Long 393 | toFloat(): Float 394 | toDouble(): Double 395 | toChar(): Char 396 | ``` 397 | 398 | ### 算术操作 399 | 400 | Kotlin支持标准的算术操作符,还支持位操作 401 | 402 | ``` 403 | shl(bits) – signed shift left (Java’s <<) 404 | shr(bits) – signed shift right (Java’s >>) 405 | ushr(bits) – unsigned shift right (Java’s >>>) 406 | and(bits) – bitwise and 407 | or(bits) – bitwise or 408 | xor(bits) – bitwise xor 409 | inv() – bitwise inversion 410 | ``` 411 | 412 | ### 字符 413 | 414 | 字符是Char类型,不能当作数字用,使用单引号包裹 415 | ```kotlin 416 | fun testChar(c: Char) { 417 | // if (c == 1) { // ERROR: incompatible types 418 | // ... 419 | // } 420 | } 421 | ``` 422 | 423 | ### 布尔值 424 | 两个值:true和false, &&和||和!三种操作 425 | 426 | ### 数组 427 | Kotlin的数组是Array类型,有get和set方法[],size属性 428 | 429 | ```kotlin 430 | //数组的部分接口 431 | class Array private constructor() { 432 | val size: Int 433 | fun get(index: Int): T 434 | fun set(index: Int, value: T): Unit 435 | fun iterator(): Iterator 436 | // ... 437 | } 438 | 439 | // 创建数组 440 | fun testArray1() { 441 | val asc = Array(5, { i -> (i * i).toString() }) 442 | } 443 | 444 | // Kotlin的数组是不可变的 445 | // 不允许将Array赋值给Array 446 | // Kotlin还有ByteArray, ShortArray, IntArray等类型 447 | fun testArray2() { 448 | val x: IntArray = intArrayOf(1, 2, 3) 449 | x[0] = x[1] + x[2] 450 | } 451 | ``` 452 | 453 | ### 字符串 454 | 和Java一样,Kotlin的字符串是不可变的,可以通过[i]访问单个字符 455 | 456 | ```kotlin 457 | fun testString1(str: String) { 458 | for (c in str) { 459 | println(c) 460 | } 461 | } 462 | ``` 463 | 464 | 字符串字面量 465 | ```kotlin 466 | fun testString2() { 467 | val s = "Hello, world!\n" 468 | // 或者三引号 469 | val text = """ 470 | for (c in "foo") 471 | print(c) 472 | """ 473 | } 474 | ``` 475 | 476 | 字符串模板 477 | ```kotlin 478 | fun stringTemplate() { 479 | val i = 10 480 | val s = "i = $i" // evaluates to "i = 10" 481 | val x = "abc" 482 | val str = "$x.length is ${x.length}" // "abc.length is 3" 483 | // 可以包含反义字符 484 | val price = "${'$'}9.99" 485 | } 486 | ``` 487 | 488 | 489 | ## 习惯用法 490 | 491 | 下面是符合Kotlin习惯的一些用法 492 | 493 | 创建Model,Kotlin中称作数据类(Data Class) 494 | 自动生成 getter/setter/equals/hashCode/toString/copy等 495 | ```kotlin 496 | data class Customer(val name: String, val email: String) 497 | ``` 498 | 499 | 函数参数支持默认值 500 | ```kotlin 501 | fun foo(a: Int = 0, b: String = "") { 502 | // do something 503 | } 504 | ``` 505 | 506 | 集合数据过滤 507 | 508 | ```kotlin 509 | fun filters(list: Collection) { 510 | val positives1 = list.filter { x -> x > 0 } 511 | // 也可以这样 512 | val positives2 = list.filter { it > 0 } 513 | } 514 | ``` 515 | 516 | 字典数据遍历 517 | ```kotlin 518 | fun maps(map: Map) { 519 | for ((k, v) in map) { 520 | println("$k -> $v") 521 | } 522 | } 523 | ``` 524 | 525 | 不可变List 526 | ```kotlin 527 | fun readOnlyList() { 528 | val list = listOf("a", "b", "c") 529 | // list.add("d") 错误:不能修改元素 530 | } 531 | ``` 532 | 533 | 不可变Map 534 | ```kotlin 535 | fun readOnlyMap() { 536 | val map1 = mapOf("a" to 1, "b" to 2, "c" to 3) 537 | println(map1["a"]) 538 | // map2["key"] = 123 错误:不能修改元素 539 | } 540 | ``` 541 | 542 | 延迟计算属性 543 | ```kotlin 544 | class LazyDemo { 545 | // val p:String by lazy{ 546 | // // compute the string 547 | // } 548 | } 549 | ``` 550 | 551 | 支持给已有类型扩展属性和函数,非常强大好用的特性 552 | ```kotlin 553 | fun Int.biggerThanTen(): Boolean { 554 | return this > 10 555 | } 556 | 557 | // 测试一下扩展函数 558 | fun extensions() { 559 | 100.biggerThanTen() 560 | 5.biggerThanTen() 561 | } 562 | ``` 563 | 564 | 创建单例对象不要太简单 565 | ```kotlin 566 | object Resource { 567 | val name = "Name" 568 | } 569 | ``` 570 | 571 | 可空类型的使用 572 | ```kotlin 573 | //nullable用法 574 | fun testNullable() { 575 | val files = File("Test").listFiles() 576 | println(files?.size) 577 | println(files?.size ?: "empty") 578 | } 579 | ``` 580 | 581 | when表达式的另一个例子 582 | ```kotlin 583 | fun transform(color: String): Int { 584 | return when (color) { 585 | "Red" -> 0 586 | "Green" -> 1 587 | "Blue" -> 2 588 | else -> throw IllegalArgumentException("Invalid color param value") 589 | } 590 | } 591 | ``` 592 | 593 | 异常的使用和Java几乎一样,除了类型定义在后面 594 | ```kotlin 595 | fun tryCatch() { 596 | val result = try { 597 | // do something 598 | } catch (e: ArithmeticException) { 599 | throw IllegalStateException(e) 600 | } 601 | 602 | // Working with result 603 | } 604 | ``` 605 | 606 | if表达式的另一个例子,可以有返回值 607 | ```kotlin 608 | fun ifExp(param: Int) { 609 | val result = if (param == 1) { 610 | "one" 611 | } else if (param == 2) { 612 | "two" 613 | } else { 614 | "three" 615 | } 616 | } 617 | ``` 618 | 619 | with的用法,先看一个例子,后面再细说 620 | ```kotlin 621 | class Turtle { 622 | fun penDown() { } 623 | fun penUp() {} 624 | fun turn(degrees: Double) { } 625 | fun forward(pixels: Double) { } 626 | } 627 | 628 | // 如果你要调用某个对象的多个函数,可以这样用 629 | fun withExp() { 630 | val turtle = Turtle() 631 | with(turtle) { //draw a 100 pix square 632 | penDown() 633 | for (i in 1..4) { 634 | forward(100.0) 635 | turn(90.0) 636 | } 637 | penUp() 638 | } 639 | } 640 | ``` 641 | 642 | ### 编码风格 643 | 644 | * 使用驼峰不要使用下划线 645 | * 类型名是用大写字母开头 646 | * 方法和属性名小写开头 647 | * 使用四个空格的缩进 648 | * 公开方法应该有文档 649 | 650 | 冒号前后有空格,大括号两边有空格,箭头两边有空格,举例 651 | 652 | ```kotlin 653 | list.filter { it > 10 }.map { element -> element * 2 } 654 | ``` 655 | 656 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | # Kotlin入门和使用(讲稿) 2 | 3 | zhangxiaoke@douban.com 2016.03.24 4 | 5 | ------ 6 | 7 | ## Intro 8 | 9 | ### Java 有哪些问题? 10 | 11 | - 空引用(Null references):连空引用的发明者都成这是个 billion-dollar 错误(参见)。不论你费多大的功夫,你都无法避免它。因为 Java 的类型系统就是不安全的。 12 | 13 | - 原始类型(Raw types):我们在开发的时候总是会为了保持兼容性而卡在范型原始类型的问题上,我们都知道要努力避免 raw type 的警告,但是它们毕竟是在语言层面上的存在,这必定会造成误解和不安全因素。 14 | 15 | - 协变数组(Covariant arrays):你可以创建一个 string 类型的数组和一个 object 型的数组,然后把 string 数组分配给 object 数组。这样的代码可以通过编译,但是一旦你尝试在运行时分配一个数给那个数组的时候,他就会在运行时抛出异常。 16 | 17 | - Java 8 存在高阶方法( higher-order functions ),但是他们是通过 SAM 类型 实现的。SAM 是一个单个抽象方法,每个函数类型都需要一个对应的接口。如果你想要创建一个并不存在的 lambda 的时候或者不存着对应的函数类型的时候,你要自己去创建函数类型作为接口。 18 | 19 | - 泛型中的通配符:诡异的泛型总是难以操作,难以阅读,书写,以及理解。对编译器而言,异常检查也变得很困难。 20 | 21 | - 不够灵活,缺乏扩展能力:我们不能给不是我们自己写的 types、classes 或者 interfaces 增加新的方法。长时间以来,我们都会采用 util 类,杂乱无章地堆砌着我们代码或者或者揉在同一个 util package 里面。如果这是解决方案的话,它肯定不理想。 22 | 23 | - 语法繁琐,不够简洁:Java 肯定不是最简洁的语言。这件事本身不是件坏事,但是事实上存在太多的常见的冗余。这会带来潜在的错误和缺陷。在这之前,我们还要处理安卓 API 带来的问题。 24 | 25 | ------ 26 | 27 | ## Features 28 | 29 | - Lambdas 30 | - Data classes 31 | - Function literals 32 | - Extension functions 33 | - Null safety 34 | - Smart casts 35 | - String templates 36 | - Properties 37 | - Class delegation 38 | - Type inference 39 | - Range expressions 40 | 41 | ### Kotlin 有几个核心的目标: 42 | 43 | - 简约:帮你减少实现同一个功能的代码量。 44 | - 易懂:让你的代码更容易阅读,同时易于理解。 45 | - 安全:移除了你可能会犯错误的功能。 46 | - 通用:基于 JVM 和 Javascript,你可以在很多地方运行。 47 | - 互操作性:这就意味着 Kotlin 和 Java 可以相互调用,目标是 100% 兼容。 48 | 49 | ### Kotlin的特性 50 | 51 | 刚才我们提到过的这些缺陷,Kotlin 通常直接移除了那些特性。同时它也加了一些新的特性: 52 | 53 | - Lambda 表达式 54 | - 数据类 (Data classes) 55 | - 函数字面量和内联函数(Function literals & inline functions) 56 | - 函数扩展 (Extension functions) 57 | - 空安全(Null safety) 58 | - 智能转换(Smart casts) 59 | - 字符串模板(String templates) 60 | - 主构造函数(Primary constructors) 61 | - 类委托(Class delegation) 62 | - 类型推断(Type inference) 63 | - 单例(Singletons) 64 | - 声明点变量(Declaration-site variance) 65 | - 区间表达式(Range expressions) 66 | 67 | 我们将在这篇文章里提及以上大多数特性。Kotlin 之所以能跟随者 JVM 的生态系统不断地进步,是因为他没有任何限制。它编译出来的正是 JVM 字节码。在 JVM 看来,它就跟其他语言一样样的。事实上,如果你在 IntelliJ 或者 Android Studio 上用 Kotlin 的插件,它自带里一个字节码查看器,可以显示每个方法生成的 JVM 字节码。 68 | 69 | ------ 70 | 71 | # Syntax 72 | 73 | ------ 74 | 75 | ## Variables 76 | 77 | ```kotlin 78 | // hello.kt 79 | package com.kotlin.demo 80 | 81 | val a: Int = 1 82 | val b = 1 83 | val c: Int 84 | c = 1 85 | // c = 2 86 | var x = 5 87 | x += 1 88 | 89 | // val text: String = "Hello, World" 90 | val text = "Hello, World" 91 | 92 | // val ints: Array = arrayOf(1,2,3,4) 93 | val ints = arrayOf(1,2,3,4) 94 | 95 | var a: String = "abc" 96 | a = null // compilation error 97 | 98 | var b: String? = "abc" 99 | b = null // ok 100 | ``` 101 | 102 | ### 类型声明 103 | 104 | - 包的声明应处于源文件顶部,目录与包的结构无需匹配,源代码可以在文件系统的任意位置。 105 | - 在 Kotlin 里,得把参数名放在前面,参数类型放在后面,用一个冒号隔开。 106 | - 常量(使用 val 关键字声明),相当于Java里的final,如果没有初始值,声明常量时,常量的类型不能省略。 107 | - 变量(使用 var 关键字声明),类型可以省略,自动推断出 Int 类型 108 | - 正如 Java 和 JavaScript,Kotlin 支持行注释及块注释,与 Java 不同的是,Kotlin 的块注释可以嵌套。 109 | - 当某个变量的值可以为 null 的时候,必须在声明处的类型后添加 ? 来标识该引用可为空。 110 | 111 | ### 类型推导 112 | 113 | 你可能在其他语言中看到过类型推导。在 Java 里,我们需要自己声明类型,变量名,以及数值。 114 | 115 | 在 Kotlin 里,顺序有些不一样,你先声明变量名,然后是类型,然后是分配值。很多情况下,你不需要声明类型。一个字符串字面量足以指明这是个字符串类型。字符,整形,长整形,单浮点数,双浮点数,布尔值都是可以无需显性声明类型的。 116 | 117 | 只要 Kotlin 可以推导,这个规则同样适用与其他一些类型。通常,如果是局部变量,当你在声明一个值或者变量的时候你不需要指明类型。在一些无法推导的场景里,你才需要用完整的声明变量语法指明变量类型。 118 | 119 | ------ 120 | 121 | ## Define Function 122 | 123 | ```kotlin 124 | fun sum(a: Int, b: Int): Int { 125 | return a + b 126 | } 127 | 128 | fun sum(a: Int, b: Int) = a + b 129 | 130 | fun printSum(a: Int, b: Int): Unit { 131 | print(a + b) 132 | } 133 | 134 | fun printSum(a: Int, b: Int) { 135 | print(a + b) 136 | } 137 | 138 | fun main(args: Array) { 139 | println("Hello, World!") 140 | } 141 | ``` 142 | 143 | ### 定义函数 144 | 145 | - 声明函数的关键字是 fun,fun 后面跟的是函数的名称,然后括号包裹起来的是函数参数,这个跟 Java 类似。 146 | 147 | - 这是带有两个 Int 参数、返回 Int 的函数。可以将表达式作为函数体、返回值类型自动推断的函数。函数返回无意义的值,Unit 返回类型可以省略。Kotlin的函数和Java类似,但不需要定义在类内部。 148 | 149 | - 函数的返回类型在最后,这个跟 Java 放在前面形式不太一样。如果一个函数没有返回任何类型,可以返回一个 Unit 类型,当然也可以省略。调用 Kotlin 标准库中的函数 println 就能打印 Hello World 出来,实际上它最终调用了 Java 的 system.out.println。 150 | 151 | ------ 152 | 153 | ## If Expressions 154 | 155 | ```kotlin 156 | fun max(a: Int, b: Int): Int { 157 | if (a > b) 158 | return a 159 | else 160 | return b 161 | } 162 | 163 | fun max(a: Int, b: Int) = if (a > b) a else b 164 | 165 | // As expression 166 | val max = if (a > b) a else b 167 | 168 | val max = if (a > b) { 169 | print("Choose a") 170 | a 171 | } 172 | else { 173 | print("Choose b") 174 | b 175 | } 176 | ``` 177 | 178 | ### If表达式 179 | 180 | - 在 Kotlin 中,if是一个表达式,即它会返回一个值。 因此就不需要三元运算符(条件 ? 然后 : 否则),因为普通的 if 就能胜任这个角色。 181 | 182 | - if的分支可以是代码块,最后的表达式作为该块的值。如果你使用 if 作为表达式而不是语句(例如:返回它的值或者 把它赋给变量),该表达式需要有 else 分支。 183 | 184 | ------ 185 | 186 | ## Loop 187 | 188 | ```kotlin 189 | fun forLoop1(args: Array) { 190 | for (arg in args) { 191 | print(arg) 192 | } 193 | } 194 | 195 | fun forLoop2(args: Array) { 196 | for (i in args.indices) { 197 | print(args[i]) 198 | } 199 | } 200 | 201 | fun whileLoop1(args: Array) { 202 | var i = 0 203 | while (i < args.size) 204 | print(args[i++]) 205 | } 206 | ``` 207 | 208 | ### FOR循环 209 | 210 | - for 循环可以对任何提供迭代器(iterator)的对象进行遍历,循环体可以是一个代码块。 211 | 212 | - for 可以循环遍历任何提供了迭代器的对象。即: 213 | - 有一个成员函数或者扩展函数 iterator(),它的返回类型 214 | - 有一个成员函数或者扩展函数 next(),并且 215 | - 有一个成员函数或者扩展函数 hasNext() 返回 Boolean 216 | 217 | - 对数组的 for 循环会被编译为并不创建迭代器的基于索引的循环,注意这种“在区间上遍历”会编译成优化的实现而不会创建额外对象,如果你想要通过索引遍历一个数组或者一个 list,你可以用.indices或array.withIndex。 218 | 219 | - while 和 do..while 照常使用。 220 | 221 | ------ 222 | 223 | ## When 224 | 225 | ```kotlin 226 | when (x) { 227 | 0, 1 -> print("x == 0 or x == 1") 228 | 3 -> print("x == 3") 229 | 4 -> print("x == 4") 230 | else -> print("otherwise") 231 | } 232 | 233 | when (x) { 234 | in 1..10 -> print("x is in the range") 235 | in validNumbers -> print("x is valid") 236 | !in 10..20 -> print("x is outside the range") 237 | else -> print("none of the above") 238 | } 239 | ``` 240 | 241 | ### When语句和表达式 242 | 243 | - when 取代了类 C 语言的 switch 操作符 244 | - when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件 245 | - when 既可以被当做表达式使用也可以被当做语句使用 246 | - 如果它被当做表达式, 符合条件的分支的值就是整个表达式的值 247 | - 如果当做语句使用, 则忽略个别分支的值 248 | - 如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔 249 | - 我们可以用任意表达式(而不只是常量)作为分支条件 250 | - 我们也可以检测一个值在(in)或者不在(!in)一个区间或者集合中 251 | - 另一种可能性是检测一个值是(is)或者不是(!is)一个特定类型的值 252 | - when 也可以用来取代 if-else if链 253 | - 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支 254 | 255 | ------ 256 | 257 | ## Returns 258 | 259 | ```kotlin 260 | return. 261 | break. 262 | continue. 263 | 264 | loop@ for (i in 1..100) { 265 | for (j in 1..100) { 266 | if (...) 267 | break@loop 268 | } 269 | } 270 | 271 | fun foo() { 272 | ints.forEach lit@ { 273 | if (it == 0) return@lit 274 | print(it) 275 | } 276 | } 277 | ``` 278 | 279 | ### 返回和跳转 280 | 281 | - Kotlin 有三种结构化跳转操作符 282 | - return.默认从最直接包围它的函数或者匿名函数返回。 283 | - break.终止最直接包围它的循环。 284 | - continue.继续下一次最直接包围它的循环。 285 | 286 | - 在 Kotlin 中任何表达式都可以用标签(label)来标记。 287 | 288 | - 我们可以用标签限制 break 或者continue,标签限制的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代。 289 | 290 | - Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。 标签限制的 return 允许我们从外层函数返回。 最重要的一个用途就是从 lambda 表达式中返回。 291 | 292 | - 这个 return 表达式从最直接包围它的函数即 foo 中返回。 (注意,这种非局部的返回只支持传给内联函数的 lambda 表达式。) 如果我们需要从 lambda 表达式中返回,我们必须给它加标签并用以限制 return。 293 | 294 | ------ 295 | 296 | ## Strings 297 | 298 | ```kotlin 299 | for (c in str) { 300 | println(c) 301 | } 302 | 303 | val s = "Hello, world!\n" 304 | 305 | val text = """ 306 | for (c in "foo") 307 | print(c) 308 | """ 309 | 310 | val s = "abc" 311 | val str = "$s.length is ${s.length}" 312 | 313 | var args = arrayOf("Cat", "Dog", "Rabbit") 314 | print("Hello ${args[0]}") 315 | ``` 316 | 317 | ### 字符串说明 318 | 319 | - 字符串用 String 类型表示。字符串是不可变的。 字符串的元素——字符可以使用索引运算符访问: s[i]。 可以用 for 循环迭代字符串。 320 | 321 | - Kotlin 有两种类型的字符串字面值: 转义字符串可以有转义字符,以及原生字符串白可以包含换行和任意文本。转义字符串很像 Java 字符串。 322 | 323 | - 原生字符串 使用三个引号(""")分界符括起来,内部没有转义并且可以包含换行和任何其他字符。 324 | 325 | - 字符串可以包含模板表达式 ,即一些小段代码,会求值并把结果合并到字符串中。 模板表达式以美元符($)开头,由一个简单的名字构成,或者用花括号扩起来的任意表达式。 326 | 327 | - 原生字符串和转义字符串内部都支持模板。 如果你需要在原生字符串中表示字面值 $ 字符(它不支持反斜杠转义),你可以用三引号语法。 328 | 329 | - 字符串字面值用单引号括起来: '1'。 特殊字符可以用反斜杠转义。 支持这几个转义序列:\t、 \b、\n、\r、\'、\"、\\ 和 \$。 编码其他字符要用 Unicode 转义序列语法:'\uFF00'。 330 | 331 | ------ 332 | 333 | ## Basic Types 334 | 335 | ```kotlin 336 | Type Bit width 337 | -------------------------------------- 338 | Double 64 339 | Float 32 340 | Long 64 341 | Int 32 342 | Short 16 343 | Byte 8 344 | ``` 345 | 346 | ```kotlin 347 | val a: Int = 10000 348 | print(a === a) // Prints 'true' 349 | val boxedA: Int? = a 350 | val anotherBoxedA: Int? = a 351 | print(boxedA === anotherBoxedA) // !!!Prints 'false'!!! 352 | ``` 353 | 354 | ### 数据类型 355 | 356 | - 在 Kotlin 中,所有东西都是对象,在这个意义上讲所以我们可以在任何变量上调用成员函数和属性。有些类型是内置的,因为他们的实现是优化过的。但是用户看起来他们就像普通的类。本节我们会描述大多数这些类型:数字、字符、布尔和数组。 357 | 358 | - Kotlin 处理数字在某种程度上接近 Java,但是并不完全相同。例如,对于数字没有隐式拓宽转换(如 Java 中 int 可以隐式转换为long——译者注),另外有些情况的字面值略有不同。 359 | 360 | - 注意在 Kotlin 中字符不是数字。 361 | 362 | - 在 Java 平台数字是物理存储为 JVM 的原生类型,除非我们需要一个可空的引用(如 Int?)或泛型。 后者情况下会把数字装箱。 363 | 364 | - 由于不同的表示方式,较小类型并不是较大类型的子类型。 如果它们是的话,就会出现下述问题。 365 | 366 | ```kotlin 367 | // 假想的代码,实际上并不能编译: 368 | val a: Int? = 1 // 一个装箱的 Int (java.lang.Integer) 369 | val b: Long? = a // 隐式转换产生一个装箱的 Long (java.lang.Long) 370 | print(a == b) // 惊!这将打印 "false" 鉴于 Long 的 equals() 检测其他部分也是 Long 371 | ``` 372 | 373 | - 因此较小的类型不能隐式转换为较大的类型。 这意味着在不进行显式转换的情况下我们不能把 Byte 型值赋给一个 Int 变量。 374 | 375 | - 缺乏隐式类型转换并不显著,因为类型会从上下文推断出来,而算术运算会有重载做适当转换。例如 376 | 377 | ```kotlin 378 | val l = 1L + 3 // Long + Int => Long 379 | ``` 380 | 381 | ### 编码风格 382 | 383 | - 使用驼峰不要使用下划线 384 | - 类型名是用大写字母开头 385 | - 方法和属性名小写开头 386 | - 使用四个空格的缩进 387 | - 公开方法应该有文档 388 | - 冒号前后有空格,大括号两边有空格,箭头两边有空格,举例 389 | 390 | ```kotlin 391 | list.filter { it > 10 }.map { element -> element * 2 } 392 | ``` 393 | 394 | ------ 395 | 396 | class: center, middle 397 | 398 | # Classes and Objects 399 | 400 | ------ 401 | 402 | ## Classes 403 | 404 | ```kotlin 405 | class Invoice { 406 | } 407 | 408 | class Empty 409 | 410 | class Person(name: String) { 411 | } 412 | 413 | class Person(name: String) { 414 | val customName = name.toUpperCase() 415 | } 416 | 417 | class Person(val firstName: String, val lastName: String, 418 | var age: Int) { 419 | // ... 420 | } 421 | ``` 422 | 423 | ### 定义一个类 424 | 425 | - 类的定义要通过 class 关键字,跟 Java 里的一样,关键字后是类名。Kotlin 有一个主构造函数,我们可以直接将构造函数参数列表写在类的声明处,还可以直接用 var 或者 val 关键字将参数声明为成员变量(又称:类属性) 426 | 427 | - 这个类声明被花括号包围,包括类名、类头(指定其类型参数,主构造 函数等)和这个类的主干。类头和主干都是可选的; 如果这个类没有主干,花括号可以被省略。 428 | 429 | - 在Kotlin中的类可以有主构造函数和一个或多个二级构造函数。主构造 函数是类头的一部分:它跟在这个类名后面(和可选的类型参数)。 430 | 431 | - 如果这个主构造函数没有任何注解或者可见的修饰符,这个constructor 关键字可以被省略。 432 | 433 | - 请注意,主构造的参数可以在初始化模块中使用。它们也可以在 类体内声明初始化的属性。事实上,声明属性和初始化主构造函数,Kotlin有简洁的语法 434 | 435 | ```kotlin 436 | class Person(val firstName: String, val lastName: String, var age: Int) { 437 | // ... 438 | } 439 | ``` 440 | 441 | ------ 442 | 443 | ## Constructors 444 | 445 | ```kotlin 446 | class Person constructor(name: String) { 447 | val fixName = name.toUpperCase() 448 | } 449 | 450 | class Person { 451 | constructor(parent: Person) { 452 | parent.children.add(this) 453 | } 454 | } 455 | 456 | class Person(name: String) { 457 | init { 458 | logger.info("Person initialized with value ${name}") 459 | } 460 | } 461 | 462 | val invoice = Invoice() 463 | val customer = Customer("Joe Smith") 464 | ``` 465 | 466 | ### 构造函数 467 | 468 | Kotlin 中,类可以拥有多个构造函数,这一点跟 Java 类似。但你也可以有一个主构造函数。下面的例子是我们从上面的例子里衍生出来的,在函数头里添加了一个主构造函数 469 | 470 | 当然,更好的方法是:直接在主构造函数里定义这些属性,定义的方法是在参数名前加上 var 或者 val 关键字,val 是代表属性是常量。 471 | 472 | 在主构造函数里,可以直接用这些参数变量赋值给类的属性,或者用构造代码块来实现初始化。 473 | 474 | - 这个主构造函数不能包含任何的代码。初始化的代码可以被放置 在initializer blocks(初始的模块),以init为关键字作为前缀 475 | - 与普通属性一样,主构造函数中声明的属性可以是 可变的(var)或者是只读的(val) 476 | - 如果构造函数有注解或可见性修饰符,这个constructor关键字是必需的 477 | - 类也可以拥有被称为”二级构造函数”(为了实现Kotlin向Java一样拥有多个构造函数),通常被加上前缀constructor 478 | - 如果类有一个主构造函数,每个二级构造函数需要委托给主构造函数, 直接或间接地通过另一个二级函数。委托到另一个使用同一个类的构造函数 用this关键字 479 | - 要创建一个类的实例,我们调用构造函数,就好像它是普通的函数 480 | 481 | ------ 482 | 483 | ## Inheritance 484 | 485 | ```kotlin 486 | class Example // Implicitly inherits from Any 487 | 488 | open class Base(p: Int) 489 | 490 | class Derived(p: Int) : Base(p) 491 | ``` 492 | 493 | ```kotlin 494 | open class Base { 495 | open fun v() {} 496 | fun nv() {} 497 | } 498 | class Derived() : Base() { 499 | override fun v() {} 500 | } 501 | 502 | open class AnotherDerived() : Base() { 503 | final override fun v() {} 504 | } 505 | ``` 506 | 507 | ### 类的继承 508 | 509 | - 在Kotlin所有的类中都有一个共同的父类Any,这是一个默认的父类且没有父类型声明 510 | - Any不属于java.lang.Object;特别是,它并没有任何其他任何成员,甚至连equals(),hashCode()和toString()都没有。 511 | - 要声明一个明确的父类,我们把类型放到类头冒号之后,父类可以(并且必须)在声明继承的地方, 用原始构造函数初始化。 512 | - 如果类没有主构造,那么每个次级构造函数初始化基本类型 使用super{:.keyword}关键字,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的二级构造函数可以调用基类型的不同的构造函数。 513 | 514 | ------ 515 | 516 | ## Overriding Members 517 | 518 | ```kotlin 519 | open class A { 520 | open fun f() { print("A") } 521 | fun a() { print("a") } 522 | } 523 | 524 | interface B { 525 | fun f() { print("B") } // interface members are 'open' by default 526 | fun b() { print("b") } 527 | } 528 | 529 | class C() : A(), B { 530 | // The compiler requires f() to be overridden: 531 | override fun f() { 532 | super.f() // call to A.f() 533 | super.f() // call to B.f() 534 | } 535 | } 536 | ``` 537 | 538 | ### 成员覆盖 539 | 540 | - 我们之前提到过,Kotlin力求清晰显式。不像Java中,Kotlin需要明确的 标注覆盖的成员(我们称之为open)和重写的函数。(继承父类并覆盖父类函数时,Kotlin要求父类必须有open标注,被覆盖的函数必须有open标注,并且子类的函数必须加override标注。) 541 | 542 | - Derived.v()函数上必须加上override标注。如果没写,编译器将会报错。 如果父类的这个函数没有标注open,则子类中不允许定义同名函数,不论加不加override。 在一个final类中(即没有声明open的类),函数上也不允许加open标注。 543 | 544 | - 成员标记为override{:.keyword}的本身是开放的,也就是说,它可以在子类中重写。如果你想禁止重写的,使用final{:.keyword}关键字 545 | 546 | - 在Kotlin中,实现继承的调用通过以下规则:如果一个类继承父类成员的多种实现方法,可以直接在子类中引用, 它必须重写这个成员,并提供其自己的实现(当然也可以使用父类的)。 为了表示从中继承的实现而采取的父类型,我们使用super{:.keyword}在尖括号,如规范的父名super 547 | 548 | - 类和其中的某些实现可以声明为abstract{:.keyword}。 抽象成员在本类中可以不用实现。 549 | 550 | - 需要注意的是,我们并不需要标注一个抽象类或者函数为open - 因为这不言而喻。我们可以重写一个open非抽象成员使之为抽象的。 551 | 552 | ------ 553 | 554 | ## Properties 555 | 556 | ```kotlin 557 | public class Address { 558 | public val code:Int = 10015 559 | public var name: String = ... 560 | public var city: String = ... 561 | public var state: String? = ... 562 | public var zip: String = ... 563 | } 564 | 565 | fun copyAddress(address: Address): Address { 566 | val result = Address() // there's no 'new' keyword in Kotlin 567 | result.name = address.name // accessors are called 568 | result.street = address.street 569 | // ... 570 | return result 571 | } 572 | 573 | const val DEPRECATED: String = "deprecated" 574 | const val SOCKET_TIMEOUT = 30*1000L 575 | ``` 576 | 577 | ### 类的属性 578 | 579 | - Kotlin的类可以有属性. 这些声明是可变的,用关键字var或者使用只读关键字val 580 | - 要使用一个属性,只需要使用名称引用即可,就相当于Java中的公共字段 581 | - 注意公有的API(即public和protected)的属性,类型是不做推导的。~~ ~~这么设计是为了防止改变初始化器时不小心改变了公有API。 582 | 583 | ### 常量属性的要求 584 | 585 | Properties the value of which is known at compile time can be marked as compile time constants using the const modifier. Such properties need to fulfil the following requirements: 586 | 587 | - Top-level or member of an object 588 | - Initialized with a value of type String or a primitive type 589 | - No custom getter 590 | 591 | ------ 592 | 593 | ## Getters and Setters 594 | 595 | ```kotlin 596 | val isEmpty: Boolean 597 | get() = this.size == 0 598 | 599 | var stringRepresentation: String 600 | get() = this.toString() 601 | set(value) { 602 | setDataFromString(value) 603 | } 604 | 605 | var setterVisibility: String = "abc" 606 | // Initializer required, not a nullable type 607 | private set // the setter is private 608 | ``` 609 | 610 | ### Getter和Setter 611 | 612 | 声明一个属性的完整语法 613 | 614 | ```kotlin 615 | var : [= ] 616 | [] 617 | [] 618 | ``` 619 | 620 | - 上面的定义中,初始器(initializer)、getter 和 setter 都是可选的。属性类型- (PropertyType)如果可以从初始器或者父类中推导出来,也可以省略。 621 | 622 | - 一个只读属性的语法和一个可变的语法有两方面的不同:1、只读属性的用val开始代替var 2、只读属性不许setter。 623 | 624 | - 如果你需要改变一个访问器或注释的可见性,但是不需要改变默认的实现, 您可以定义访问器而不定义它的实例。 625 | 626 | - 在Kotlin不能有字段。然而,有时有必要有使用一个字段在使用定制的访问器的时候。对于这些目的,Kotlin提供 自动支持,在属性名后面使用 field标识符。 627 | 628 | - 编译器会查看访问器的内部, 如果他们使用了实际字段(或者访问器使用默认实现),那么将会生成一个实际字段,否则不会生成。 629 | 630 | ------ 631 | 632 | ## Interface 633 | 634 | ```kotlin 635 | interface MyInterface { 636 | val property: Int // abstract 637 | 638 | val propertyWithImplementation: String 639 | get() = "foo" 640 | 641 | fun bar() 642 | fun foo() { 643 | // optional body 644 | } 645 | } 646 | 647 | class Child : MyInterface { 648 | override val property: Int = 29 649 | 650 | override fun bar() { 651 | // body 652 | } 653 | } 654 | ``` 655 | 656 | ### 接口定义 657 | 658 | - 使用关键字 interface 来定义接口。Kotlin 的接口与 Java 8 类似,既包含抽象方法的声明,也包含 实现。与抽象类不同的是,接口无法保存状态。它可以有 属性但必须声明为 abstract或提供访问器实现。 659 | 660 | - 一个类或者对象可以实现一个或多个接口。 661 | 662 | - 实现多个接口时,可能会遇到接口方法名同名的问题。D 可以不用重写 bar(),因为它实现了 A 和 B,因而可以自动继承 B 中 bar() 的实现, 但是两个接口都定义了方法 foo(),为了告诉编译器 D 会继承谁的方法,必须在 D 中重写 foo()。 663 | 664 | ------ 665 | 666 | ## Data Class 667 | 668 | ```kotlin 669 | // equals()/hashCode()/toString()/componentN()/copy() 670 | 671 | data class User(val name: String, val age: Int) 672 | data class User(val name: String = "", val age: Int = 0) 673 | 674 | fun copy(name: String = this.name, age: Int = this.age) 675 | = User(name, age) 676 | 677 | val jack = User(name = "Jack", age = 1) 678 | val olderJack = jack.copy(age = 2) 679 | 680 | val jane = User("Jane", 35) 681 | val (name, age) = jane 682 | println("$name, $age years of age") 683 | // prints "Jane, 35 years of age" 684 | ``` 685 | 686 | ### 数据类/POJO 687 | 688 | - 我们经常创建一些只是处理数据的类。在这些类里的标准功能经常是 衍生自数据。在Kotlin中,这叫做 数据类 并标记为data。 689 | 690 | - 编译器自动从在主构造函数定义的全部特性中得到以下成员: 691 | - equals()/hashCode() 对, 692 | - toString() 格式是 "User(name=John, age=42)", 693 | - componentN() functions 对应按声明顺序出现的所有属性, 694 | - copy() 函数(见下面)。 695 | 696 | - 如果有某个函数被明确地定义在类里或者被继承,编译器就不会生成这个函数。 697 | 698 | - 在JVM中,如果生成的类需要含有一个无参的构造函数,则所有的属性必须有默认值。 699 | 700 | - 在很多情况下,我们我们需要对一些属性做修改而其他的不变。 这就是copy()这个方法的来源。对于上文的User类,应该是这么实现这个方法。 701 | - 在标准库提供了Pair和Triple。在很多情况下,即使命名数据类是一个更好的设计选择, 因为这能让代码可读性更强。 702 | 703 | ------ 704 | 705 | ## Destructuring Declarations 706 | 707 | ```kotlin 708 | data class Person(val name:String, val age:Int){} 709 | 710 | val person = Person("John",20) 711 | val (name, age) = person 712 | 713 | val name = person.component1() 714 | val age = person.component2() 715 | 716 | for ((a, b) in collection) { ... } 717 | for ((key, value) in map) { 718 | } 719 | 720 | val (result, status) = function(...) 721 | 722 | operator fun Map.iterator(): Iterator> 723 | = entrySet().iterator() 724 | operator fun Map.Entry.component1() = getKey() 725 | operator fun Map.Entry.component2() = getValue() 726 | ``` 727 | 728 | ### 解构声明 729 | 730 | - 有时把一个对象_解构_成很多变量很比较方便,这种语法叫做_解构声明_。一个解构声明同时创造多个变量。 我们申明了两个新变量:name 和 age,并且可以独立使用他们。 731 | 732 | - component1() 和 component2() 函数是 principle of conventions widely 在Kotlin 中的另一个例子。 (参考运算符如 + , *, for-loops 等) 任何可以被放在解构声明右边的和组件函数的需求数字都可以调用它。 当然,这里可以有更多的如 component3() 和 component4()。 733 | 734 | - 变量 a 和 b 从调用从 component1() 和 component2() 返回的集合collection中的对象。 735 | 736 | - 让我们从一个函数中返回两个变量。例如,一个结果对象和一些排序的状态。 在Kotlin中一个简单的实现方式是申明一个data class并且返回他的实例。 737 | 738 | - 可能最好的遍历一个映射的方式就是这样,实现这个接口,于是你可以自由的使用解构声明 for-loops 来操作映射(也可以用在数据类实例的集合等)。 739 | 740 | ------ 741 | 742 | ## Nested and Inner Classes 743 | 744 | ```kotlin 745 | class Outer { 746 | private val bar: Int = 1 747 | class Nested { 748 | fun foo() = 2 749 | } 750 | } 751 | 752 | val demo = Outer.Nested().foo() // == 2 753 | ``` 754 | 755 | ```kotlin 756 | class Outer { 757 | private val bar: Int = 1 758 | inner class Inner { 759 | fun foo() = bar 760 | } 761 | } 762 | 763 | val demo = Outer().Inner().foo() // == 1 764 | ``` 765 | 766 | ### 嵌套类和内部类 767 | 768 | - 在类的内部可以嵌套其他的类,相当于Java里的static内部类。 769 | 770 | - 为了能被外部类访问一个类可以被标记为内部类(“inner” 关键词)。 内部类会带有一个来自外部类的对象的引用 771 | 772 | ------ 773 | 774 | ## Enum 775 | 776 | ```kotlin 777 | enum class Direction { 778 | NORTH, SOUTH, WEST, EAST 779 | } 780 | 781 | enum class Color(val rgb: Int) { 782 | RED(0xFF0000), 783 | GREEN(0x00FF00), 784 | BLUE(0x0000FF) 785 | } 786 | 787 | enum class ProtocolState { 788 | WAITING { 789 | override fun signal() = TALKING 790 | }, 791 | 792 | TALKING { 793 | override fun signal() = WAITING 794 | }; 795 | 796 | abstract fun signal(): ProtocolState 797 | } 798 | ``` 799 | 800 | ### 枚举 801 | 802 | - 枚举类的最基本应用是实现类型安全的多项目集合。其中每一个常量(NORTH,SOUTH……)都是一个对象。每一个常量用逗号“,”分隔。 803 | 804 | - 枚举实例也可以被声明为他们自己的匿名类,并同时包含他们相应原本的方法和覆盖基本方法。注意如果枚举类定义了任何成员,你需要像JAVA一样把枚举实例的定义和成员定义用分号分开。 805 | 806 | - 像JAVA一样,枚举类在Kotlin中有合成方法。它允许列举枚举实例并且通过名称返回枚举实例。下面是应用实例 (假设枚举实例名称是EnumClass)。 807 | 808 | - 枚举常量也可以实现Comparable 接口。他们会依照在枚举类中的定义先后以自然顺序排列。 809 | ------ 810 | 811 | ## Object 812 | 813 | ```kotlin 814 | val adHoc = object { 815 | var x: Int = 0 816 | var y: Int = 0 817 | } 818 | print(adHoc.x + adHoc.y) 819 | 820 | // Singleton 821 | object Resource { 822 | val name = "Name" 823 | } 824 | 825 | open class A(x: Int) { 826 | public open val y: Int = x 827 | } 828 | 829 | interface B {...} 830 | 831 | val ab = object : A(1), B { 832 | override val y = 15 833 | } 834 | ``` 835 | 836 | ### 对象和单例 837 | 838 | - 有些时候我们需要创建一个对某些类做了轻微改变的一个对象,而不用为了它显式地定义一个新的子类。 Java把这种情况处理为匿名内部类。 在Kotlin稍微推广了这个概念,称它们为对象表达式和对象声明。 839 | 840 | - 对象表达式:如果父类型有一个构造函数,合适的构造函数参数必须传递给它。 多个父类型用逗号隔开,跟在冒号后面。 841 | 842 | - 或许,我们需要的仅是无父类的一个对象,那么我们可以简单地写为adHoc这种。 843 | - 就像Java的匿名内部类,在对象表达式里代码可以访问封闭的作用域 (但与Java不同的是,它能访问非final修饰的变量)。 844 | 845 | - 对象声明:单例模式是一种非常有用的模式,而在Kotilin(在Scala之后)中很容易就能声明一个单例。DataProviderManager被称为对象声明。如果有一个object关键字在名字前面,这不能再被称为一个_表达式_。 我们不能把这样的东西赋值给变量,但我们可以通过它的名字来引用它。 846 | 847 | 对象表达式与对象声明语义上的不同 848 | 849 | - 当对象声明被第一次访问的时候,它会被延迟(lazily)初始化 850 | - 当对象表达式被用到的时候,它会被立即执行(并且初始化) 851 | 852 | ------ 853 | 854 | ## Companion 855 | 856 | ```kotlin 857 | class MyClass { 858 | companion object Factory { 859 | fun create(): MyClass = MyClass() 860 | } 861 | } 862 | 863 | interface Factory { 864 | fun create(): T 865 | } 866 | class MyClass { 867 | companion object : Factory { 868 | override fun create(): MyClass = MyClass() 869 | } 870 | } 871 | 872 | val x = MyClass.Companion 873 | ``` 874 | 875 | ### 伴生对象 876 | 877 | Kotlin 移除了 static 的概念。通常用 companion object 来实现类似功能。 878 | 879 | - 伴生对象:一个对象声明在一个类里可以标志上companion这个关键字 880 | - 伴生对象的成员可以使用类名称作为限定符来调用 881 | - 使用companion关键字时候,伴生对象的名称可以省略 882 | - 注意,虽然伴生对象的成员在其他语言中看起来像静态成员,但在运行时它们 仍然是实体的实例成员,举例来说,我们能用它实现接口 883 | - 然而,在JVM中,如果你使用@JvmStatic注解,你可以让伴生对象的成员生成为实际存在的静态方法和域 884 | 885 | ------ 886 | 887 | ## Class Delegation 888 | 889 | ```kotlin 890 | interface Base { 891 | fun print() 892 | } 893 | 894 | class BaseImpl(val x: Int) : Base { 895 | override fun print() { print(x) } 896 | } 897 | 898 | class Derived(b: Base) : Base by b 899 | 900 | fun main() { 901 | val b = BaseImpl(10) 902 | Derived(b).print() // prints 10 903 | } 904 | ``` 905 | 906 | ### 类的委托 907 | 908 | 委托是一个大家都知道的设计模式,Kotlin 把委托视为很重要的语言特性。 909 | 910 | - 委托模式是实现继承的一个有效方式。Kotlin原生支持它。一个类 Derived 可以从一个接口 Base继承并且委托所有的共有方法为具体对象。 911 | 912 | - 在父类Derived中的 by-语句表示 b 将会被 储存在 Derived 的内部对象中,并且编译器会生成所有的用于转发给b的Base的方法。 913 | 914 | ------ 915 | 916 | ## Delegated Properties 917 | 918 | ```kotlin 919 | class Example { 920 | var p: String by Delegate() 921 | } 922 | 923 | class Delegate { 924 | operator fun getValue(thisRef: Any?, 925 | property: KProperty<*>): String { 926 | return "$thisRef, thank you for delegating 927 | '${property.name}' to me!" 928 | } 929 | 930 | operator fun setValue(thisRef: Any?, 931 | property: KProperty<*>, value: String) { 932 | println("$value has been assigned to 933 | '${property.name} in $thisRef.'") 934 | } 935 | } 936 | 937 | val e = Example() 938 | println(e.p) 939 | // Example@33a17727, thank you for delegating ‘p’ to me! 940 | 941 | e.p = "NEW" 942 | // NEW has been assigned to ‘p’ in Example@33a17727. 943 | ``` 944 | 945 | ### 委托属性 946 | 947 | 有一些种类的属性,虽然我们可以在每次需要的时候手动实现它们,但是如果能够把他们只实现一次 并放入一个库同时又能够一直使用它们那会更好。例如: 948 | 949 | - 延迟属性(lazy properties): 数值只在第一次被访问的时候计算。 950 | - 可观察属性(observable properties): 监听器得到关于这个特性变化的通知, 951 | - 把所有属性储存在一个map中,而不是每个在单独的字段里。 952 | 为了支持这些(或者其他)例子,Kotlin 采用 委托属性。 953 | 954 | 当我们读取一个Delegate的委托实例 p , Delegate中的getValue()就被调用, 所以它第一变量就是我们从 p 读取的实例,第二个变量代表 p 自身的描述。 (例如你可以用它的名字)。 955 | 956 | 类似的,当我们给 p 赋值, setValue() 函数就被调用. 前两个参数是一样的,第三个参数保存着将要被赋予的值 957 | 958 | ### 属性委托要求 959 | 960 | 这里我们总结委托对象的要求。 961 | 962 | 对于一个 只读 属性 (如 val), 一个委托一定会提供一个 getValue函数来获取下面的参数: 963 | 964 | - 接收者 — 必须与_属性所有者_类型相同或者是其父类(对于扩展属性,类型范围允许扩大), 965 | - 包含数据 — 一定要是 KProperty<*> 的类型或它的父类型, 966 | - 这个函数必须返回同样的类型作为属性(或者子类型) 967 | 968 | 对于一个 可变 属性 (如 var), 一个委托需要额外地提供一个函数 setValue 来获取下面的参数: 969 | 970 | - 接收者 — 同 getValue(), 971 | - 包含数据 — 同 getValue(), 972 | - 新的值 — 必须和属性同类型或者是他的父类型。 973 | 974 | getValue() 或/和 setValue() 函数可能会作为代理类的成员函数或者扩展函数来提供。 当你需要代理一个属性给一个不是原来就提供这些函数的对象的时候,后者更为方便。 两种函数都需要用operator关键字来进行标记 975 | 976 | ### 标准委托 977 | 978 | 标准库中对于一些有用的委托提供了工厂(factory)方法。 979 | 980 | ------ 981 | 982 | ## By Lazy 983 | 984 | ```kotlin 985 | val lazyValue: String by lazy { 986 | Log.v("Lazy", "Lazy Init") 987 | "Hello, Lazy!" 988 | } 989 | 990 | fun lazyTest() { 991 | Log.d("Lazy", lazyValue) 992 | Log.d("Lazy", lazyValue) 993 | } 994 | ``` 995 | 996 | ```shell 997 | V/Lazy: Lazy Init 998 | D/Lazy: Hello, Lazy! 999 | D/Lazy: Hello, Lazy! 1000 | ``` 1001 | 1002 | ### 延迟属性 Lazy 1003 | 1004 | 函数 lazy() 接受一个 lambda 然后返回一个可以作为实现延迟属性的委托 Lazy 实例来: 第一次对于 get()的调用会执行(之前)传递到 lazy()的lamda表达式并记录结果, 后面的 get() 调用会直接返回记录的结果。 1005 | 1006 | 默认地,对于lazy属性的计算是同步锁(synchronized) 的: 这个值只在一个线程被计算,并且所有的线程会看到相同的值。如果初始化代理的同步锁不是必须的,以至于多个线程可以同步地执行,那么将LazyThreadSafetyMode.PUBLICATION作为一个变量传递给lazy()函数。 1007 | 1008 | 而且如果你确定初始化将总是发生在单个线程,那么你可以使用 LazyThreadSafetyMode.NONE 模式, 它不会有任何线程安全的保证和相关的开销。 1009 | 1010 | ------ 1011 | 1012 | ## Observable 1013 | 1014 | ```kotlin 1015 | import kotlin.properties.Delegates 1016 | 1017 | class User { 1018 | var name: String by Delegates.observable("") { 1019 | prop, old, new -> 1020 | println("$old -> $new") 1021 | } 1022 | } 1023 | 1024 | fun main(args: Array) { 1025 | val user = User() 1026 | user.name = "first" 1027 | user.name = "second" 1028 | } 1029 | 1030 | // output 1031 | // -> first 1032 | // first -> second 1033 | ``` 1034 | 1035 | ### 可观察属性 Observable 1036 | 1037 | Delegates.observable() 需要两个参数:初始值和handler。 这个 handler 会在每次我们给赋值的时候被调用 (在工作完成前). 它有三个参数:一个被赋值的属性,旧的值和新的值 1038 | 1039 | 这个例子输出: 1040 | 1041 | -> first 1042 | first -> second 1043 | 如果你想有能力来截取和“否决”它分派的事件,就使用 vetoable() 取代 observable(). 被传递给 vetoable 的handler会在属性被赋新的值_之前_执行 1044 | 1045 | ------ 1046 | 1047 | ## By Map 1048 | 1049 | ```kotlin 1050 | class User(val map: Map) { 1051 | val name: String by map 1052 | val age: Int by map 1053 | } 1054 | 1055 | val user = User(mapOf( 1056 | "name" to "John Doe", 1057 | "age" to 25 1058 | )) 1059 | 1060 | println(user.name) // Prints "John Doe" 1061 | println(user.age) // Prints 25 1062 | 1063 | class MutableUser(val map: MutableMap) { 1064 | var name: String by map 1065 | var age: Int by map 1066 | } 1067 | 1068 | // custom impl by Json 1069 | ``` 1070 | 1071 | ### 把属性储存在map中 1072 | 1073 | 一个参加的用例是在一个map里存储属性的值。 这经常出现在解析JSON或者做其他的“动态”的事情应用里头。 在这样的情况下,你需要使用map的实例本身作为代理用于代理属性 1074 | 1075 | 在这个例子中,构造函数会接收一个map参数,委托会从这个map中取值 (通过string类型的key,就是属性的名字),对于 var的变量,我们可以把只读的Map换成 MutableMap就可以了 1076 | 1077 | ------ 1078 | 1079 | # Functions and Lambdas 1080 | 1081 | ------ 1082 | 1083 | ## Function Declarations 1084 | 1085 | ```kotlin 1086 | fun double(x: Int): Int { 1087 | } 1088 | val result = double(2) 1089 | 1090 | infix fun Int.shl(x: Int): Int { 1091 | ... 1092 | } 1093 | 1 shl 2 1094 | 1095 | fun read(b: Array, off: Int = 0, len: Int = b.size()) { 1096 | ... 1097 | } 1098 | 1099 | fun printHello(name: String?): Unit { 1100 | if (name != null) 1101 | println("Hello ${name}") 1102 | } 1103 | 1104 | fun printHello(name: String?) { 1105 | ... 1106 | } 1107 | ``` 1108 | 1109 | ### 函数声明 1110 | 在Kotlin中,函数声明使用关键字 fun 1111 | 1112 | ### 函数用途 1113 | 调用函数使用传统的方法,调用成员函数使用点表达式 1114 | 1115 | ### 中缀表示法 1116 | 1117 | 函数还可以用中缀表示法,当:1. 他们是成员函数 或者 扩展函数;2. 他们只有一个参数;3. 使用infix关键字声明 1118 | 1119 | ### 单个表达式函数 1120 | 当一个函数返回单个表达式,花括号可以省略并且主体由** =**符号之后指定。显式地声明返回类型可选时,这可以由编译器推断。 1121 | 1122 | ### 显式地返回类型 1123 | 函数模块体必须显式地指定返回类型,除非是用于返回Unit, 在这种情况下,它是可选的。 Kotlin不推断返回类型与函数在模块体的功能,因为这些功能可能在模块体有复杂的控制流程, 对于阅读者(有时甚至编译器)来说返回类型将不明显。 1124 | 1125 | ### 返回Unit的函数 1126 | 如果一个函数不返回任何有用的值,它的返回类型是Unit。Unit是一种只有一个值 - Unit`。这个 值不需要显式地返回。Unit返回类型声明也是可选的,可以省略。 1127 | 1128 | ------ 1129 | 1130 | ## Function Arguments 1131 | 1132 | ```kotlin 1133 | fun reformat(str: String, 1134 | normalizeCase: Boolean = true, 1135 | upperCaseFirstLetter: Boolean = true, 1136 | wordSeparator: Char = ' ') { 1137 | ... 1138 | } 1139 | 1140 | reformat(str, true, true, false, '_') 1141 | reformat(str, 1142 | normalizeCase = true, 1143 | upperCaseFirstLetter = true, 1144 | wordSeparator = '_' 1145 | ) 1146 | reformat(str, wordSeparator = '_') 1147 | 1148 | val a = arrayOf(1, 2, 3) 1149 | val list = asList(-1, 0, *a, 4) 1150 | ``` 1151 | 1152 | ### 参数 1153 | 函数参数是使用Pascal表达式,即 name: type。参数用逗号隔开。每个参数必须有显式类型。 1154 | 1155 | ### 默认参数(缺省参数) 1156 | 函数参数有默认值,当对应的参数是省略。与其他语言相比可以减少数量的过载。默认值定义使用后* * = * *类型的值。 1157 | 1158 | ### 命名参数 1159 | 可以在调用函数时使用命名的函数参数。当一个函数有大量的参数或默认参数时这非常方便。使用命名参数我们可以使代码更具有可读性。可以省略部分参数。 1160 | 1161 | ------ 1162 | 1163 | ## Function Usage 1164 | 1165 | ```kotlin 1166 | fun double(x: Int): Int = x * 2 1167 | fun double(x: Int) = x * 2 1168 | 1169 | fun asList(vararg ts: T): List { 1170 | val result = ArrayList() 1171 | for (t in ts) // ts is an Array 1172 | result.add(t) 1173 | return result 1174 | } 1175 | 1176 | val list = asList(1, 2, 3) 1177 | 1178 | val a = arrayOf(1, 2, 3) 1179 | val list = asList(-1, 0, *a, 4) 1180 | 1181 | class Sample() { 1182 | fun foo() { print("Foo") } 1183 | } 1184 | 1185 | Sample().foo() 1186 | ``` 1187 | 1188 | ### 数量可变的参数(可变参数) 1189 | 函数的(通常最后一个)参数可以使用’vararg`修饰。内部函数vararg类型T是可见的arrayT,即上面的例子中的ts变量是Array类型。当我们调用vararg函数,我们可以一个接一个传递参数,例如 asList(1, 2, 3)或者,如果我们已经有了一个数组 并希望将其内容传递给函数,我们使用spread 操作符(在数组前面加*) 1190 | 1191 | ### 函数作用域(函数范围) 1192 | 在Kotlin中函数可以在文件顶级声明,这意味着您不需要像一些语言如Java、C#或Scala那样创建一个类来持有一个函数。此外 除了顶级函数功能,Kotlin函数也可以在局部声明,作为成员函数和扩展函数. 1193 | 1194 | ### 局部函数 1195 | Kotlin提供局部函数,即一个函数在另一个函数中,局部函数可以访问外部函数的局部变量(即闭包),所以在上面的例子,the visited是局部变量. 1196 | 1197 | ### 成员函数 1198 | 成员函数是一个函数,定义在一个类或对象里,成员函数调用点符号 1199 | 1200 | ------ 1201 | 1202 | ## Higher-order Functions 1203 | 1204 | ```kotlin 1205 | fun lock(lock: Lock, body: () -> T): T { 1206 | lock.lock() 1207 | try { 1208 | return body() 1209 | } 1210 | finally { 1211 | lock.unlock() 1212 | } 1213 | } 1214 | ``` 1215 | 1216 | ### 高阶函数 1217 | 1218 | 这是个新奇的术语,它指的是函数可以接收函数,或者函数可以返回函数。 1219 | 1220 | 高阶函数是一种能用函数作为参数或者返回值为函数的一种函数。 lock()是高阶函数中一个比较好的例子,它接收一个lock对象和一个函数,获得锁,运行传入的函数,并释放锁。 1221 | 1222 | 我们分析一下上面的代码:函数body拥有函数类型:() -> T 所以body应该是一个不带参数并且返回T类型的值的函数。 它在try代码块中调用,被lock保护的,当lock()函数被调用时返回他的值。 1223 | 1224 | 如果我们想调用lock()函数,我们可以把另一个函数传递给它作为参数(详见 函数引用)。 1225 | 1226 | ### 内联函数 1227 | 1228 | 使用内联函数有时能提高高阶函数的性能。 1229 | 1230 | ------ 1231 | 1232 | ## Higher-order Functions 1233 | 1234 | ```kotlin 1235 | fun List.map(transform: (T) -> R): List { 1236 | val result = arrayListOf() 1237 | for (item in this) 1238 | result.add(transform(item)) 1239 | return result 1240 | } 1241 | 1242 | val doubled = ints.map { it -> it * 2 } 1243 | 1244 | strings filter {it.length == 5} sortBy {it} map {it.toUpperCase()} 1245 | 1246 | val names = listOf("Jake", "Jesse", "Matt", "Alec") 1247 | val jakes = names.filter { it == "Jake" } 1248 | ``` 1249 | 1250 | ### 高阶函数 1251 | 1252 | 另一个高阶函数的例子是 map() ( MapReduce) 1253 | 1254 | 这些约定可以写成 LINQ-风格 的代码: 1255 | 1256 | ```kotlin 1257 | strings filter {it.length == 5} sortBy {it} map {it.toUpperCase()} 1258 | ``` 1259 | 1260 | 在Kotlin中, 如果函数的最后一个参数是一个函数,那么该参数可以在括号外指定: 1261 | 1262 | ```kotlin 1263 | lock (lock) { 1264 | sharedResource.operation() 1265 | } 1266 | ``` 1267 | 1268 | ------ 1269 | 1270 | ## Function Types 1271 | ```kotlin 1272 | fun max(collection: Collection, less: (T, T) -> Boolean): T? { 1273 | var max: T? = null 1274 | for (it in collection) 1275 | if (max == null || less(max, it)) 1276 | max = it 1277 | return max 1278 | } 1279 | 1280 | max(strings, { a, b -> a.length() < b.length() }) 1281 | 1282 | fun compare(a: String, b: String): Boolean = a.length() < b.length() 1283 | ``` 1284 | 1285 | ### 函数类型 1286 | 1287 | 对于一个接收一个函数作为参数的函数,我们必须为该参数指定一个函数类型。 1288 | 1289 | max函数是一个高阶函数, 也就是说 他的第二个参数是一个函数. 这个参数是一个表达式,但它本身也是一个函数, 也就是函数字面量。 1290 | 1291 | 参数 less 是一个 (T, T) -> Boolean类型的函数, 也就是说less函数接收两个T类型的参数并返回一个Boolean值: 如果第一个比第二个小就返回True. 1292 | 1293 | 在第四行代码里, less 被用作为一个函数: 它传入两个T类型的参数. 1294 | 1295 | 如上所写的是就函数类型, 或者还有命名参数, 如果你想文档化每个参数的含义。 1296 | 1297 | ------ 1298 | 1299 | ## Lambda Expressions 1300 | 1301 | ```kotlin 1302 | val sum = { x: Int, y: Int -> x + y } 1303 | 1304 | val sum: (Int, Int) -> Int = { x, y -> x + y } 1305 | 1306 | ints.filter { it > 0 } 1307 | 1308 | fun(x: Int, y: Int): Int = x + y 1309 | 1310 | fun(x: Int, y: Int): Int { 1311 | return x + y 1312 | } 1313 | 1314 | ints.filter(fun(item) = item > 0) 1315 | 1316 | // receier 1317 | val sum = fun Int.(other: Int): Int = this + other 1318 | sum : Int.(other: Int) -> Int 1319 | 1.sum(2) 1320 | ``` 1321 | 1322 | ### Lambda表达式 1323 | 1324 | 另外,函数表达式也被称作 lambdas 或者 closures。这里有一个最简单的函数表达式: { it.toString() }。它是一段代码在 “it” 变量上调用了 two-string 函数。“it” 是个 built-in 的名字。当你在写这些函数表达式的时候,如果你只有一个参数传入这段代码,你可以用 “it” 引用,这只是一个你不需要定义参数的方法。 1325 | 1326 | 但是当你需要定义参数的时候,或者不止一个参数要定义的时候,语法就是这样的: { x, y -> x + y }。我们可以创建一段代码,一个函数表达式,输入两个参数,然后把它们相加。如果我们愿意,我们可以显示定义类型。 1327 | 1328 | ### Lambda表达式语法 1329 | 1330 | Lambda 表达式的全部语法形式, 也就是函数类型的字面量, 譬如上面的sum的声明。 1331 | 1332 | 一个 lambda 表达式货匿名函数是一个“函数字面值”, 即 一个未声明的函数,但却立即写为表达式。 1333 | 1334 | 这是非常常见的,一个 lambda 表达式只有一个参数。 如果Kotlin能自己计算出自己的数字签名,我们就可以不去声明这个唯一的参数。并且用 it进行隐式声明。 1335 | 1336 | 请注意,如果函数取另一个函数作为最后一个参数,该 lambda 表达式参数可以放在 括号外的参数列表。 语法细则详见 callSuffix. 1337 | 1338 | ### lambda 表达式 1339 | 1340 | 这里有更详细的描述, 但是为了继续这一段,让我们看到一个简短的概述: 1341 | 1342 | - 一个 lambda 表达式总是被大括号包围着。 1343 | - 其参数(如果有的话)被声明在->之前(参数类型可以省略) 1344 | - 函数体在 -> 后面 (如果存在的话). 1345 | 1346 | ### 匿名函数 1347 | 1348 | 上述 lambda 表达式的语法还少了一个东西: 能够指定函数的返回 类型。在大多数情况下, 这是不必要的。因为返回类型可以被自动推断出来. 然而,如果你需要要明确的指定。你需要一个替代语法。匿名函数看起来很像一个普通函数声明, 只是名字被省略了。 1349 | 1350 | ```kotlin 1351 | fun(x: Int, y: Int): Int = x + y 1352 | ``` 1353 | 1354 | ### 闭包 1355 | 1356 | 一个 lambda 表达式或者匿名函数(以及一个本地函数本地函数和一个 对象表达式) 可以访问他的_闭包_,即声明在外范围内的变量。与java不同,在闭包中捕获的变量可以被修改。 1357 | 1358 | ### 带接收者得函数字面值 1359 | 1360 | 这样的函数字面量的类型是一个带receiver的函数类型 1361 | 1362 | kotlin提供了使用一个特定的 receiver对象 来调用一个函数的能力. 在函数体内部,你可以调用 接受者对象 的方法而不需要任何额外的限定符。 这和 扩展函数 有点类似,它允你在函数体内访问接收器对象的成员。 1363 | 1364 | ------ 1365 | 1366 | ## Inline Functions 1367 | 1368 | ```kotlin 1369 | lock(l) { foo() } 1370 | 1371 | // compiled 1372 | l.lock() 1373 | try { 1374 | foo() 1375 | } 1376 | finally { 1377 | l.unlock() 1378 | } 1379 | 1380 | inline fun lock(lock: Lock, body: () -> T): T { 1381 | // ... 1382 | } 1383 | 1384 | inline fun foo(inlined: () -> Unit, 1385 | noinline notInlined: () -> Unit) { 1386 | // ... 1387 | } 1388 | ``` 1389 | 1390 | ### 内联函数 1391 | 1392 | 内联函数和高阶函数经常一起见到。在某些场景下,当你用到泛型的时候,你可以给函数加上 inline 关键字。在编译时,它会用 lambda 表达式替换掉整个函数,整个函数的代码会成为内联代码。 1393 | 1394 | 使用高阶函数会带来一些运行时间效率的损失:每一个函数都是一个对象,并且都会捕获一个闭包。 即那些在函数体内会被访问的变量。 内存分配(对于函数对象和类)和虚拟调用会引入运行时间开销。 1395 | 1396 | 但是在许多情况下通过内联化 lambda 表达式可以消除这类的开销。 我们通过下面的示例函数来分析上面这些内容。如,lock() 函数可以被很容易地在调用点被内联。 1397 | 1398 | inline修饰符会影响函数体本身以及传递过来的lambdas: 所有的这些会被内联到 调用点。内联本身有时会引起生成的代码数量增加,但是如果我们使用得当(不要内联大的函数)。它将在 性能上有所提升,尤其是在超多态(megamorphic)调用点的循环中。 1399 | 1400 | ### 禁止内联(noinline) 1401 | 1402 | 为了预防 有时候你只希望被(作为参数)传递到一个内联函数的lamdas 只有一些被内联,你可以用 noinline 修饰符标记你的参数 1403 | 1404 | ------ 1405 | 1406 | ## Extensions 1407 | 1408 | ```kotlin 1409 | fun MutableList.swap(index1: Int, index2: Int) { 1410 | val tmp = this[index1] // 'this' corresponds to the list 1411 | this[index1] = this[index2] 1412 | this[index2] = tmp 1413 | } 1414 | 1415 | val l = mutableListOf(1, 2, 3) 1416 | l.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'l' 1417 | 1418 | val List.lastIndex: Int 1419 | get() = size - 1 1420 | 1421 | fun Date.isTuesday(): Boolean { 1422 | return getDay() == 2 1423 | } 1424 | 1425 | val tuesday = date.isTuesday(); 1426 | 1427 | fun Int.biggerThanTen(): Boolean { 1428 | return this > 10 1429 | } 1430 | ``` 1431 | 1432 | 1433 | 声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。 下面是为MutableList添加一个swap方法,这个this关键字在扩展方法内接受对应的对象(在点符号以前传过来的) 现在,我们可以像一个其他方法一样调用MutableList。 1434 | 1435 | ### 函数扩展 1436 | 1437 | Kotlin和c#、Gosu一样,能够扩展一个类的新功能,而无需继承类或使用任何类型的设计模式,如装饰者。 这通过特殊的声明叫做_extensions_。Kotlin支持_extension functions_ 和 extension properties. 1438 | 1439 | 函数扩展是 Kotlin 最强大的特性之一。 1440 | 1441 | 函数扩展可以是任何整形,字面量或者包装类型,也可以在标记为 final 的类上做类似操作。因为扩展函数不是真的给类增加代码,任何人都没有办法去修改一个类,它实际上是创建了一个静态方法,用语法糖来让扩展函数看着像是类自带的方法一样。 1442 | 1443 | Kotlin 有扩展函数的概念。这不是 Kotlin 语言独有的,但是和其他语言里面我们看到的扩展又不太一样。如果我们在纯 Java 语言的环境下添加一个 date 的方法,我们需要写一个 utils 类或者 dates 类,然后增加一个静态方法。它接收一个实例,然后做些事情,可能会返回一个值。 1444 | 1445 | Kotlin 的一个非常好的功能是,它会自动地转换有 getters 和 setters 综合属性的类型。所以我能够替换 getDay() 为 day,因为这个 day 的属性是存在的。它看起来像一个 field,但是实际上是个 property – getter 和 setter 的概念融合在了一起。 1446 | 1447 | 1448 | ### 扩展的静态解析 1449 | 1450 | 扩展不能真正的修改他们继承的类。通过定义一个扩展,你不能在类内插入新成员, 仅仅是通过该类的实例用点表达式去调用这个新函数。 1451 | 1452 | 我们想强调下扩展方法是被静态分发的,即他们不是接收类型的虚方法。 1453 | 1454 | ### Nullable接收者 1455 | 1456 | 注意扩展可被定义为可空的接收类型。这样的扩展可以被对象变量调用, 即使他的值是null,你可以在方法体内检查this == null,这也允许你 在没有检查null的时候调用Kotlin中的toString():检查发生在扩展方法的内部的时候 1457 | 1458 | ### 扩展属性 1459 | 1460 | 和方法相似,Kotlin支持扩展属性。注意:由于扩展没有实际的将成员插入类中,因此对扩展来说是无效的 属性是有backing field.这就是为什么初始化其不允许有 扩展属性。他们的行为只能显式的使用 getters/setters. 1461 | 1462 | ### 伴生对象的扩展 1463 | 1464 | 如果一个类定义有一个伴生对象 ,你也可以为伴生对象定义 扩展函数和属性,就像伴生对象的其他普通成员,只用用类名作为限定符去调用他们。 1465 | 1466 | ------ 1467 | 1468 | # Stdlib 1469 | 1470 | Kotlin标准库 The Kotlin Standard Library provides living essentials for everyday work with Kotlin. These include: 1471 | 1472 | - 有用的高阶函数 Higher-order functions implementing idiomatic patterns (let, apply, use, synchronized, etc). 1473 | 1474 | - 集合操作的扩展函数 Extension functions providing querying operations for collections (eager) and sequences (lazy). 1475 | 1476 | - 字符串等工具函数 Various utilities for working with strings and char sequences. 1477 | 1478 | - 对JDK文件/线程/IO等类的扩展 Extensions for JDK classes making it convenient to work with files, IO, and threading. 1479 | 1480 | ------ 1481 | 1482 | ## Types 1483 | 1484 | ```java 1485 | Java Kotlin 1486 | 1487 | java.lang.Object kotlin.Any! 1488 | java.lang.Cloneable kotlin.Cloneable! 1489 | java.lang.Comparable kotlin.Comparable! 1490 | java.lang.Enum kotlin.Enum! 1491 | java.lang.Annotation kotlin.Annotation! 1492 | java.lang.Deprecated kotlin.Deprecated! 1493 | java.lang.Void kotlin.Nothing! 1494 | java.lang.CharSequence kotlin.CharSequence! 1495 | java.lang.String kotlin.String! 1496 | java.lang.Number kotlin.Number! 1497 | java.lang.Throwable kotlin.Throwable! 1498 | 1499 | int[] kotlin.IntArray! 1500 | String[] kotlin.Array<(out) String>! 1501 | ``` 1502 | 1503 | ------ 1504 | 1505 | ## Functions 1506 | 1507 | ```kotlin 1508 | fun assert(value: Boolean, lazyMessage: () -> Any) 1509 | fun check(value: Boolean, lazyMessage: () -> Any) 1510 | fun require(value: Boolean, lazyMessage: () -> Any) 1511 | fun error(message: Any): Nothing 1512 | 1513 | fun run(block: () -> R): R 1514 | fun synchronized(lock: Any, block: () -> R): R 1515 | fun lazy(initializer: () -> T): Lazy 1516 | fun lazyOf(value: T): Lazy 1517 | fun T.let(block: (T) -> R): R 1518 | fun T.apply(block: T.() -> Unit): T 1519 | fun repeat(times: Int, action: (Int) -> Unit) 1520 | fun with(receiver: T, block: T.() -> R): R 1521 | 1522 | ``` 1523 | 1524 | ------ 1525 | 1526 | ## Functions 1527 | 1528 | ```kotlin 1529 | inline fun T.apply(block: T.() -> Unit) 1530 | : T { block(); return this } 1531 | 1532 | inline fun T.let(block: (T) -> R): R = block(this) 1533 | 1534 | inline fun with(receiver: T, block: T.() -> R) 1535 | : R = receiver.block() 1536 | 1537 | var u1: User? = null 1538 | val u2 = User("Alice", age = 20, desc = "Wonderful!") 1539 | u1?.apply { sayHello() } 1540 | u2.apply { eat(); sayHello() } 1541 | u1?.let { 1542 | u2.show(it) 1543 | u1.sayHello() 1544 | } 1545 | u2.let { println("user is valid!") } 1546 | with(u2) { 1547 | println("do something on user") 1548 | sayHello() 1549 | } 1550 | ``` 1551 | 1552 | ### T.apply 1553 | 1554 | Calls the specified function block with this value as its receiver and returns this value. 1555 | 1556 | ### T.let 1557 | 1558 | Calls the specified function block with this value as its argument and returns its result. 1559 | 1560 | ### with 1561 | 1562 | Calls the specified function block with the given receiver as its receiver and returns its result. 1563 | 1564 | ------ 1565 | 1566 | ## Arrays 1567 | 1568 | ```kotlin 1569 | class Array private constructor() { 1570 | val size: Int 1571 | fun get(index: Int): T 1572 | fun set(index: Int, value: T): Unit 1573 | 1574 | fun iterator(): Iterator 1575 | // ... 1576 | } 1577 | 1578 | val asc = Array(5, { i -> (i * i).toString() }) 1579 | 1580 | val x: IntArray = arrayOf(1, 2, 3, 4, 5) 1581 | x[0] = x[1] + x[2] 1582 | 1583 | val intArray2 = intArrayOf(2, 4, 6, 8, 10) 1584 | val boolArray1 = arrayOf(true, false, true, false, true) 1585 | val boolArray2 = booleanArrayOf(true, true, false, true) 1586 | val strArray1 = arrayOf("Cat", "Dog", "Rabbit") 1587 | 1588 | // byteArrayOf(), charArrayOf(), shortArrayOf(), 1589 | // longArrayOf(), floatArrayOf() 1590 | ``` 1591 | 1592 | ------ 1593 | 1594 | ## Array Extensions 1595 | 1596 | ```kotlin 1597 | // create array 1598 | arrayOf()/arrayOfNulls()/emptyArray()/intArrayOf() 1599 | 1600 | // functions 1601 | - get()/set()/iterator()/indices/lastIndex 1602 | 1603 | // extension functions 1604 | - asIterable()/asList()/asSequence()/associate() 1605 | - distinct()/distinctBy()/drop()/dropLast()/binarySearch() 1606 | 1607 | - find()/findLast() 1608 | - fill()/contains()/copyOf()/count() 1609 | 1610 | - elementAt()/elementAtOrNull()/first()/last() 1611 | - all()/any()/filter()/filterNot()/filterTo()/flatten() 1612 | 1613 | - forEach()/forEachIndexed()/map()/mapIndexed()/groupBy() 1614 | - intersect()()/joinTo()/joinToString() 1615 | ``` 1616 | 1617 | ------ 1618 | ## Collections 1619 | 1620 | ```kotlin 1621 | val numbers: MutableList = mutableListOf(1, 2, 3) 1622 | println(numbers) // prints "[1, 2, 3]" 1623 | numbers.add(4) 1624 | 1625 | val readOnlyView: List = numbers 1626 | readOnlyView.clear() // -> does not compile 1627 | 1628 | val strings = hashSetOf("a", "b", "c", "c") 1629 | 1630 | val items = listOf(1, 2, 3, 4) 1631 | items.last == 4 1632 | items.filter { it % 2 == 0 } // Returns [2, 4] 1633 | 1634 | if (rwList.none { it > 6 }) println("No items above 6") 1635 | val item = rwList.firstOrNull() 1636 | 1637 | val readWriteMap = hashMapOf("foo" to 1, "bar" to 2) 1638 | println(map["foo"]) 1639 | val snapshot: Map = HashMap(readWriteMap) 1640 | ``` 1641 | 1642 | ------ 1643 | 1644 | ## Ranges 1645 | 1646 | ```kotlin 1647 | if (i in 1..10) { // equivalent of 1 <= i && i <= 10 1648 | println(i) 1649 | } 1650 | 1651 | for (i in 1..4) print(i) // prints "1234" 1652 | for (i in 4..1) print(i) // prints nothing 1653 | 1654 | for (i in 4 downTo 1) print(i) // prints "4321" 1655 | 1656 | for (i in 1..4 step 2) print(i) // prints "13" 1657 | 1658 | for (i in 4 downTo 1 step 2) print(i) // prints "42" 1659 | 1660 | // progression with values [1, 3, 5, 7, 9, 11] 1661 | (1..12 step 2).last == 11 1662 | 1663 | // progression with values [1, 4, 7, 10] 1664 | (1..12 step 3).last == 10 1665 | 1666 | // progression with values [1, 5, 9] 1667 | (1..12 step 4).last == 9 1668 | ``` 1669 | 1670 | ------ 1671 | 1672 | ## Collection Extensions 1673 | 1674 | ```kotlin 1675 | //Iterable, Collection, List, Set, Map 1676 | 1677 | - iterator() // Iterable 1678 | - size/indices/count()/isEmpty()/contains() // Collection 1679 | - lastIndex/get()/indexOf()/listIterator()/subList() // List 1680 | 1681 | // Extensions Functions for List 1682 | - toTypedArray()/toMutableList() 1683 | - isNotEmpty()/?orEmpty()/plus()/plusElement() 1684 | - asReversed()/binarySearch()/findLast() 1685 | 1686 | - dropLast()/takeLast()/reduceRight() 1687 | - getOrNull()/elementAt()/elementAtOrNull() 1688 | - single()/slice()/first()/firstOrNull()/last() 1689 | - +/plus() 1690 | 1691 | // MutableIterable, MutableCollection, MutableList 1692 | 1693 | - add()/addAll()/remove()/removeAt() 1694 | - removeAll()/retainAll()/clear() 1695 | - reverse()/sort()/sortBy()/sortByDescending()/sortWith() 1696 | - +=/plusAssign() -=/minusAssign() 1697 | 1698 | ``` 1699 | 1700 | ------ 1701 | 1702 | ## Generics 1703 | 1704 | ```java 1705 | - Java’s wildcards are converted into type projections 1706 | - Foo becomes Foo! 1707 | - Foo becomes Foo! 1708 | - Java’s raw types are converted into star projections 1709 | - List becomes List<*>!, i.e. List! 1710 | ``` 1711 | 1712 | ```kotlin 1713 | // Array -> Java: Array 1714 | fun copy2(from: Array, to: Array) { 1715 | // ... 1716 | } 1717 | 1718 | // Array -> Java: Array 1719 | fun fill(dest: Array, value: String) { 1720 | // ... 1721 | } 1722 | 1723 | fun T.basicToString() : String { // extension function 1724 | // ... 1725 | } 1726 | ``` 1727 | 1728 | ### Kotlin的泛型 1729 | 1730 | - 与Java相似,Kotlin中的类也具有类型参数,一般而言,创建类的实例时,我们需要声明参数的类型,但当参数类型可以从构造函数参数等途径推测时,在创建的过程中可以忽略类型参数: 1731 | 1732 | ``` 1733 | val box = Box(1) // 1 has type Int, so the compiler figures out that we are talking about Box 1734 | ``` 1735 | 1736 | - 首先,我们考虑一下Java中的通配符(wildcards)的意义。该问题在文档 Effective Java, Item 28: Use bounded wildcards to increase API flexibility中给出了详细的解释。 首先,Java中的泛型类型是不变的,即List并不是List的子类型。 原因在于,如果List是可变的,并不会 优于Java数组。 1737 | 1738 | - 通配符类型(wildcard)的声明 ? extends T表明了该方法允许一类对象是 T的子类型,而非必须得是 T本身。 这意味着我们可以安全地从元素( T的子类集合中的元素)读取 T,同时由于 我们并不知道 T的子类型,所以不能写元素。 反过来,该限制可以让Collection表示为Collection的子类型。 简而言之,带extends限定(上限)的通配符类型(wildcard)使得类型是协变的(covariant)。 1739 | 1740 | - out修饰符叫做型变注解,同时由于它在参数类型位置被提供,所以我们讨论声明处型变。 与Java的使用处型变相反,类型使用通配符使得类型协变。另外除了out,Kotlin又补充了一项型变注释:in。它是的变量类型反变:只可以被消费而不可以 被生产。反变类的一个很好的例子是 Comparable 1741 | 1742 | ### Kotlin中的Java泛型 1743 | 1744 | Kotlin的泛型和Java的有些不同(详见 Generics)。当引入java类型的时候,我们作如下转换: 1745 | 1746 | - Java的通配符转换成类型投射 1747 | - Foo 转换成 Foo! 1748 | - Foo 转换成 Foo! 1749 | - Java的原始类型转换成星号投射 1750 | - List 转换成 List<*>!, 也就是 List! 1751 | 1752 | 和Java一样,Kotlin在运行时不保留泛型,即对象不知道传递到他们构造器中的那些参数的的实际类型。 1753 | 1754 | Kotlin的范型就像Java一样不会在运行时保留信息,也就是对象不会携带传递到它们构造函数中的类型参数的信息。也就是说,运行时无法区分ArrayList() 和 ArrayList(). 1755 | 1756 | 也就是,ArrayList() 和 ArrayList() 是区分不出来的。 这意味着,不可能用 is-来检测泛型。 1757 | 1758 | 这就导致,无法使用is-检测范型。~~ Kotlin只允许用is-来检测星号投射的泛型类型: Kotlin只允许用is-检测星号投射的范型类型。 1759 | 1760 | ------ 1761 | 1762 | # Others 1763 | 1764 | ------ 1765 | 1766 | ## Smart Cast 1767 | 1768 | ```kotlin 1769 | fun demo(x: Any) { 1770 | if (x is String) { 1771 | print(x.length) // x is automatically cast to String 1772 | } 1773 | } 1774 | 1775 | if (x !is String) return 1776 | print(x.length) // x is automatically cast to String 1777 | 1778 | // x is automatically cast to string 1779 | // on the right-hand side of `||` 1780 | if (x !is String || x.length == 0) return 1781 | 1782 | // x is automatically cast to string 1783 | // on the right-hand side of `&&` 1784 | if (x is String && x.length > 0) 1785 | // x is automatically cast to String 1786 | print(x.length) 1787 | 1788 | ``` 1789 | 1790 | ### is 和 !is运算符 1791 | 我们可以使用is 或者它的否定!is运算符检查一个对象在运行中是否符合所给出的类型 1792 | 1793 | ### 智能转换 1794 | 在很多情况下,在Kotlin有时不用使用明确的转换运算符,因为编译器会在需要的时候自动为了不变的值和输入(安全)而使用is进行监测。这些智能转换在 when-expressions 和 while-loops 也一样 1795 | 1796 | ------ 1797 | 1798 | ## Type Cast 1799 | 1800 | ```kotlin 1801 | when (x) { 1802 | is Int -> print(x + 1) 1803 | is String -> print(x.length + 1) 1804 | is IntArray -> print(x.sum()) 1805 | } 1806 | ``` 1807 | 1808 | ```kotlin 1809 | val x: String = y as String 1810 | 1811 | val x: String? = y as String? 1812 | 1813 | val x: String? = y as? String 1814 | ``` 1815 | 1816 | ### “不安全”的转换运算符 1817 | 1818 | 通常,如果转换是不可能的,转换运算符会抛出一个异常。于是,我们称之为不安全的。在Kotlin这种不安全的转换会出现在插入运算符as (see operator precedence): 1819 | 1820 | ```kotlin 1821 | val x: String = y as String 1822 | ``` 1823 | 1824 | 记住null不能被转换为不可为空的String。例如,如果y是空,则这段代码会抛出异常。为了匹配Jave的转换语义,我们不得不在右边拥有可空的类型,就像: 1825 | 1826 | ```kotlin 1827 | val x: String? = y as String? 1828 | ``` 1829 | 1830 | ### “安全的”(可为空的)转换运算符 1831 | 1832 | 为了避免异常的抛出,一个可以使用安全的转换运算符——as? ,它可以在失败时返回一个null: 1833 | 1834 | ```kotlin 1835 | val x: String? = y as? String 1836 | ``` 1837 | 1838 | 记住尽管事实是右边的as?可使一个不为空的String类型的转换结果为可空的。 1839 | 1840 | ------ 1841 | 1842 | ## This 1843 | 1844 | ```kotlin 1845 | class A { // implicit label @A 1846 | inner class B { // implicit label @B 1847 | fun Int.foo() { // implicit label @foo 1848 | val a = this@A // A's this 1849 | val b = this@B // B's this 1850 | 1851 | val c = this // foo()'s receiver, an Int 1852 | val c1 = this@foo // foo()'s receiver, an Int 1853 | 1854 | val funLit = lambda@ fun String.() { 1855 | val d = this // funLit's receiver 1856 | } 1857 | val funLit2 = { s: String -> 1858 | // foo()'s receiver, since enclosing lambda expression 1859 | // doesn't have any receiver 1860 | val d1 = this 1861 | } 1862 | } 1863 | } 1864 | } 1865 | ``` 1866 | 1867 | ### This的语义 1868 | 1869 | 为了记录下当前的接受者我们使用this表达式: 1870 | 1871 | - 在一个类成员中, this指的是当前类对象。 1872 | - 在一个扩展函数或者带有接收者字面函数, this表示左边的接收者. 1873 | 1874 | 如果 this 没有应用者,则指向的是最内层的闭合范围。为了在其它范围中返回 this ,需要使用标签。 1875 | 1876 | 为了在范围外部访问this(一个类, 或者扩展函数, 或者带标签的带接收者的字面函数 我们使用this@label作为label 1877 | 1878 | ------ 1879 | 1880 | ## Null Safety 1881 | 1882 | ```kotlin 1883 | var a: String = "abc" 1884 | a = null // compilation error 1885 | 1886 | var b: String? = "abc" 1887 | b = null // ok 1888 | 1889 | val l = a.length 1890 | val l = b.length // error: variable 'b' can be null 1891 | 1892 | val l = if (b != null) b.length else -1 1893 | 1894 | val l: Int = if (b != null) b.length else -1 1895 | val l = b?.length ?: -1 1896 | val l = b!!.length() // npe 1897 | val aInt: Int? = a as? Int 1898 | 1899 | val files = File("Test").listFiles() 1900 | println(files?.size) 1901 | println(files?.size ?: "empty") 1902 | ``` 1903 | 1904 | ### 可空(Nullable)和不可空(Non-Null) 类型 1905 | 1906 | 在 Kotlin 的类型体系里,有空类型和非空类型。类型系统识别出了 string 是一个非空类型,并且阻止编译器让它以空的状态存在。想要让一个变量为空,我们需要在声明后面加一个 ? 号,同时赋值为 null。 1907 | 1908 | Kotlin 的类型系统致力于消除空引用异常的危险,又称《上亿美元的错误》。 1909 | 1910 | 许多编程语言,包括 Java 中最常见的错误就是访问空引用的成员变量,导致空引用异常。在 Java 中, 将等同于 NullPointerException 或简称 NPE 。 1911 | 1912 | Kotlin 类型系统的目的就是从我们的代码中消除 NullPointerException 。 NPE 的原因可能是 1913 | 1914 | - 显式调用 throw NullPointerException() 1915 | - Usage of the !! operator that is described below 1916 | - 外部 Java 代码引起 1917 | - 对于初始化,有一些数据不一致 (比如一个还没初始化的 this 用于构造函数的某个地方) 1918 | 1919 | 在 Kotlin 中,类型系统是要区分一个引用是否可以是 null (nullable references)或者不可以,即 不可空引用(non-null references)。 例如,常见的 String 就不能够为 null,若是想要允许 null ,我们可以声明一个变量为可空字符串,写作 String?。 1920 | 1921 | ### 安全的调用 1922 | 1923 | 你的第二个选择是安全的操作符,写作 ?. :b?.length 1924 | 1925 | 如果 b 是非空的,就会返回 b.length ,否则返回 null,这个表达式的类型就是 Int?. 1926 | 1927 | 安全调用在链式调用的时候十分有用。例如,如果Bob,一个雇员,可被分配给一个部门(或不),这反过来又可以获得 Bob 的部门负责人的名字(如果有的话),我们这么写: 1928 | 1929 | ```kotlin 1930 | bob?.department?.head?.name 1931 | ``` 1932 | 1933 | 如果任意一个属性(环节)为空,这个链式调用就会返回 null。 1934 | 1935 | ### Elvis 操作符 1936 | 1937 | 当我们有一个可以为空的变量 r,我们可以说 「如果 r 非空,我们使用它;否则使用某个非空的值: 1938 | 1939 | `val l: Int = if (b != null) b.length else -1` 1940 | 对于完整的 if-表达式, 可以换成 Elvis 操作符来表达, 写作 ?:: 1941 | 1942 | `val l = b?.length ?: -1` 1943 | 如果 ?: 的左边表达式是非空的, elvis 操作符就会返回左边的结果, 否则返回右边的内容。 1944 | 请注意,仅在左侧为空的时候,右侧表达式才会进行计算。 1945 | 1946 | 注意, 因为 throw 和 return 在 Kotlin 中都是一种表达式,它们也可以用在 Elvis 操作符的右边。非常方便,例如,检查函数参数 1947 | 1948 | ### !! 操作符 1949 | 1950 | 第三种操作的方式是给 NPE 爱好者的。我们可以写 b!! ,这样就会返回一个不可空的 b 的值(例如:在我们例子中的 String)或者如果 b 是空的,就会抛出 NPE 异常: 1951 | 1952 | `val l = b!!.length()` 1953 | 因此,如果你想要一个 NPE,你可以使用它。 1954 | 1955 | ### 安全转型 1956 | 1957 | 转型的时候,可能会经常出现 ClassCastException。 所以,现在可以使用安全转型,当转型不成功的时候,它会返回 null: 1958 | 1959 | ```kotlin 1960 | val aInt: Int? = a as? Int 1961 | ``` 1962 | 1963 | ------ 1964 | 1965 | ## Exceptions 1966 | 1967 | ```kotlin 1968 | try { 1969 | // some code 1970 | } 1971 | catch (e: SomeException) { 1972 | // handler 1973 | } 1974 | finally { 1975 | // optional finally block 1976 | } 1977 | 1978 | // try is an expression 1979 | val a: Int? = try { parseInt(input) } 1980 | catch (e: NumberFormatException) { null } 1981 | ``` 1982 | 1983 | ------ 1984 | ## Reference 1985 | 1986 | ```kotlin 1987 | val c = MyClass::class 1988 | 1989 | fun isOdd(x: Int) = x % 2 != 0 1990 | 1991 | val numbers = listOf(1, 2, 3) 1992 | println(numbers.filter(::isOdd)) // prints [1, 3] 1993 | 1994 | fun compose(f: (B) -> C, g: (A) -> B): (A) -> C { 1995 | return { x -> f(g(x)) } 1996 | } 1997 | 1998 | fun length(s: String) = s.size 1999 | 2000 | val oddLength = compose(::isOdd, ::length) 2001 | val strings = listOf("a", "ab", "abc") 2002 | 2003 | println(strings.filter(oddLength)) // Prints "[a, abc]" 2004 | ``` 2005 | 2006 | 2007 | ### 类引用 2008 | 2009 | 最基本的反射特性就是得到运行时的类引用。要获取引 用并使之成为静态类可以使用字面类语法: 2010 | 2011 | val c = MyClass::class 2012 | 引用是KClass类型.你可以使用KClass.properties 和KClass.extensionProperties来获得类和父类所有属性引用的列表。 2013 | 2014 | 注意Kotlin类引用不完全与Java类引用一致.查看Java interop section 详细信息。 2015 | 2016 | ### 函数引用 2017 | 2018 | 我们有一个像下面这样的函数声明: 2019 | 2020 | fun isOdd(x: Int) = x % 2 != 0 2021 | 我们可以直接调用(isOdd(5)), 也可以把它作为一个值传给其他函数. 我们使用::操作符实现: 2022 | 2023 | val numbers = listOf(1, 2, 3) 2024 | println(numbers.filter(::isOdd)) // prints [1, 3] 2025 | 这里 ::isOdd是一个函数类型的值 (Int) -> Boolean. 2026 | 2027 | 注意现在::不能被使用来重载函数. 将来, 我们计划 提供一个语法明确参数类型这样就可以使用明确的重载函数了。 2028 | 2029 | 如果我们需要使用类成员或者一个扩展方法,它必须是有权访问的, 例如String::toCharArray带着一个String: String.() -> CharArray类型扩展函数. 2030 | 2031 | ### 属性引用 2032 | 2033 | 我们同样可以用::操作符来访问Kotlin中的顶级类的属性: 2034 | 2035 | ```kotlin 2036 | var x = 1 2037 | 2038 | fun main(args: Array) { 2039 | println(::x.get()) // prints "1" 2040 | ::x.set(2) 2041 | println(x) // prints "2" 2042 | } 2043 | ``` 2044 | 2045 | 表达式::x推断为KProperty类型的属性对象,它允许我们 使用get()函数来读它的值或者使用name属性来得到它的值。 2046 | 2047 | ### 构造函数引用 2048 | 2049 | 构造函数可以像属性和方法那样引用. 它们可以使用在任何一个函数类型的对象的地方, 期望得到相同参数的构造函数,并返回一个适当类型的对象. 构造函数使用::操作符加类名引用.考虑如下函数, 需要一个无参数函数返回类型是Foo 2050 | 2051 | ------ 2052 | 2053 | class: center, middle 2054 | 2055 | # Java Interop 2056 | 2057 | ------ 2058 | 2059 | ## Calling Java from Kotlin 2060 | 2061 | ```kotlin 2062 | import java.util.* 2063 | 2064 | fun demo(source: List) { 2065 | val list = ArrayList() 2066 | // 'for'-loops work for Java collections: 2067 | for (item in source) 2068 | list.add(item) 2069 | // Operator conventions work as well: 2070 | for (i in 0..source.size() - 1) 2071 | list[i] = source[i] // get and set are called 2072 | } 2073 | 2074 | val calendar = Calendar.getInstance() 2075 | if (calendar.firstDayOfWeek == Calendar.SUNDAY) { 2076 | calendar.firstDayOfWeek = Calendar.MONDAY 2077 | } 2078 | ``` 2079 | 2080 | - 如果一个Java方法返回void,那么在Kotlin中,它会返回Unit。 万一有人使用它的返回值,Kotlin的编译器会在调用的地方赋值, 因为这个值本身已经提前可以预知了(这个值就是Unit)。 2081 | 2082 | ### Null安全性和平台类型 2083 | 2084 | Java中的所有引用都可能是null值,这使得Kotlin严格的null控制对来自Java的对象来说变得不切实际。 在Kotlin中Java声明类型被特别对待叫做platform types.这种类型的Null检查是不严格的, 所以他们还维持着同Java中一样的安全性 (更多参见下面)。 2085 | 2086 | ### 平台类型的概念 2087 | 2088 | 如上所述,平台类型不能再程序里显式的出现, 所以没有针对他们的语法。 然而,编译器和IDE有时需要显式他们(如在错误信息,参数信息中),所以我们用 一个好记的标记来表示他们: 2089 | 2090 | - T! 表示 “T 或者 T?” 2091 | - (Mutable)Collection! 表示 “T的java集合,可变的或不可变的,可空的或非空的” 2092 | - Array<(out) T>! 表示 “T(或T的子类)的java数组,可空的或非空的” 2093 | 2094 | ### 映射类型 2095 | 2096 | Kotlin特殊处理一部分java类型。这些类型不是通过as或is来直接转换,而是_映射_到了指定的kotlin类型上。 映射只发生在编译期间,运行时仍然是原来的类型。 java的原生类型映射成如下kotlin类型(记得 平台类型)。 2097 | 2098 | ### Java数组 2099 | 2100 | 和Java不同,Kotlin里的数组不是协变的。Kotlin不允许我们把Array 赋值给 Array, 从而避免了可能的运行时错误。Kotlin也禁止我们把一个子类的数组当做父类的数组传递进Kotlin的方法里。 但是对Java方法,这是允许的(考虑这种形式的平台类型platform types Array<(out) String>!)。 2101 | 2102 | Java平台上,原生数据类型的数组被用来避免封箱/开箱的操作开销。 由于Kotlin隐藏了这些实现细节,就得有一个变通方法和Java代码交互。 每个原生类型的数组都有一个特有类(specialized class)来处理这种问题(IntArray, DoubleArray, CharArray …)。 它们不是Array类,而是被编译成java的原生数组,来获得最好的性能。 2103 | 2104 | ------ 2105 | 2106 | ## Calling Java from Kotlin 2107 | 2108 | ```kotlin 2109 | val javaObj = JavaArray() 2110 | val array = intArrayOf(0, 1, 2, 3) 2111 | javaObj.removeIndicesVarArg(*array) 2112 | 2113 | val fooClass = foo.javaClass // foo.getClass() 2114 | val fooClass = javaClass() // Foo.class 2115 | 2116 | if (Character.isLetter(a)) { 2117 | // ... 2118 | } 2119 | 2120 | // SAM 2121 | val runnable = Runnable { println("This runs in a runnable") } 2122 | val executor = ThreadPoolExecutor() 2123 | // void execute(Runnable command) 2124 | executor.execute { println("This runs in a thread pool") } 2125 | ``` 2126 | 2127 | ### Java Varargs 2128 | 2129 | Java类也会这样声明方法,表示参数是可变参数。这种情况,你需要用展开操作符 * 来传递 IntArray,目前无法传递 null 给一个变参的方法。 2130 | 2131 | ### 对象方法 2132 | 2133 | 当java类型被引入到kotlin里时,所有的java.lang.Object类型引用,会被转换成 Any。 因为Any不是平台独有的,它仅声明了三个成员方法:toString(), hashCode() 和 equals(), 所以为了能用到java.lang.Object的其他方法,kotlin采用了扩展函数。 2134 | 2135 | ### Java 反射 2136 | 2137 | Java反射可以用在kotlin类上,反之亦然。前面提过,你可以 instance.javaClass 或者 ClassName::class.java 开始基于 java.lang.Class 的java反射操作。 2138 | 2139 | ### java类的继承 2140 | 2141 | 在kotlin里,超类里最多只能有一个java类(java接口数目不限)。这个java类必须放在超类列表的最前面。 2142 | 2143 | ### 访问静态成员 2144 | 2145 | java类的静态成员就是它们的 “同伴对象”。我们无法将这样的“同伴对象”当作数值来传递, 但可以显式的访问它们,比如: 2146 | 2147 | ### SAM(单抽象方法) 转换 2148 | 2149 | 就像 Java 8 那样,Kotlin 支持 SAM 转换,这意味着 Kotlin 函数字面量可以被自动的转换成 只有一个非默认方法的 Java 接口的实现,只要这个方法的参数类型 能够跟这个 Kotlin 函数的参数类型匹配的上。 2150 | 2151 | 如果 Java 类有多个接受函数接口的方法,你可以用一个 适配函数来把闭包转成你需要的 SAM 类型。编译器也会在必要时生成这些适配函数。 2152 | 2153 | 注意SAM的转换只对接口有效,对抽象类无效,即使它们就只有一个 抽象方法。 2154 | 2155 | 还要注意这个特性只针对和 Java 的互操作;因为 Kotlin 有合适的函数类型,把函数自动转换成 Kotlin 接口的实现是没有必要的,也就没有支持了。 2156 | 2157 | ------ 2158 | 2159 | ## Calling Kotlin from Java 2160 | 2161 | ```kotlin 2162 | @file:JvmName("DemoUtils") 2163 | package demo 2164 | class Foo 2165 | 2166 | fun bar() { 2167 | } 2168 | 2169 | class C(id: String) { 2170 | @JvmField val ID = id 2171 | } 2172 | ``` 2173 | 2174 | ```java 2175 | // Java 2176 | new demo.Foo(); 2177 | demo.DemoUtils.bar(); 2178 | ``` 2179 | 2180 | 可以使用 @JvmName 注解自定义生成的Java 类的类名 2181 | 2182 | ### 属性 2183 | 2184 | 属性getters被转换成 get-方法,setters转换成set-方法。 2185 | 2186 | ### 包级别的函数 2187 | 2188 | example.kt 文件中 org.foo.bar 包内声明的所有的函数和属性,都会被放到一个叫org.foo.bar.ExampleKt的java类里。 2189 | 2190 | ### 类名 2191 | 2192 | 如果多个文件中生成了相同的Java类名(包名相同,类名相同或者有相同的@JvmName注解)通常会报错,然而,可以在每个文件添加 @JvmMultifileClass注解,可以让编译器生成一个统一的带有特殊名字的类,这个类包含了对应这些文件中所有的声明。 2193 | 2194 | ### 实例字段 2195 | 2196 | 如果在 Java 需要像字段一样调用一个 Kotlin 的属性,你需要使用@JvmField注解。这个字段与属性具有相同的可见性。属性符合有实际字段(backing field)、非私有、没有open, override 或者 const修饰符、不是被委托的属性这些条件才可以使用@JvmField注解。 2197 | 2198 | ------ 2199 | 2200 | ## Calling Kotlin from Java 2201 | 2202 | ```kotlin 2203 | class C { 2204 | companion object { 2205 | const val VERSION = 1 2206 | 2207 | @JvmStatic fun foo() {} 2208 | fun bar() {} 2209 | 2210 | @JvmField val COMPARATOR: Comparator 2211 | = compareBy { it.value } 2212 | } 2213 | } 2214 | ``` 2215 | 2216 | ```java 2217 | C.foo(); // works fine 2218 | C.bar(); // error: not a static method 2219 | C.COMPARATOR.compare(key1, key2); 2220 | int v = C.VERSION; 2221 | ``` 2222 | 2223 | ### 静态字段 2224 | 2225 | 在一个命名对象或者伴生对象中声明的Koltin属性会持有静态实际字段(backing fields),这些字段存在于该命名对象或者伴生对象中的。 2226 | 2227 | 通常,这些字段都是private的,但是他们可以通过以下方式暴露出来。 2228 | 2229 | - @JvmField 注解; 2230 | - lateinit 修饰符; 2231 | - const 修饰符. 2232 | - 2233 | 用 @JvmField 注解该属性可以生成一个与该属性相同可见性的静态字段。 2234 | 2235 | 使用const 注解可以将 Kotlin 属性转换成 Java 中的静态字段。 2236 | 2237 | ### 静态方法 2238 | 2239 | 正如上面所说,Kotlin 自动为包级函数生成了静态方法 在Kotlin 中,还可以通过@JvmStatic注解在命名对象或者伴生对象中定义的函数来生成对应的静态方法。 2240 | 2241 | 通过使用 @JvmStatic 注解对象的属性或伴生对象,使对应的getter 和 setter 方法在这个对象或者包含这个伴生对象的类中也成为静态成员。 2242 | 2243 | ------ 2244 | 2245 | ## Calling Kotlin from Java 2246 | 2247 | ```kotlin 2248 | fun List.filterValid(): List 2249 | 2250 | @JvmName("filterValidInt") 2251 | fun List.filterValid(): List 2252 | 2253 | @JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") { 2254 | ... 2255 | } 2256 | 2257 | @Throws(IOException::class) 2258 | fun foo() { throw IOException() } 2259 | ``` 2260 | 2261 | ```java 2262 | // Java 2263 | void f(String a, int b, String c) { } 2264 | void f(String a, int b) { } 2265 | void f(String a) { } 2266 | ``` 2267 | 2268 | ### 用@JvmName解决签名冲突 2269 | 2270 | 有时我们想让一个 Kotlin 里的命名函数在字节码里有另外一个 JVM 名字。 最突出的例子就是 类名型擦除: 2271 | 2272 | fun List.filterValid(): List 2273 | fun List.filterValid(): List 2274 | 2275 | 这两个函数不能同时定义,因为它们的 JVM 签名是一样的:filterValid(Ljava/util/List;)Ljava/util/List;. 如果我们真的相让它们在 Kotlin里用同一个名字,我们需要用@JvmName去注释它们中的一个(或两个),指定的另外一个名字当参数 2276 | 2277 | Kotlin里它们可以都用filterValid来访问,但是在Java里,它们是filterValid 和 filterValidInt. 同样的技巧也适用于属性 x 和函数 getX() 共存 2278 | 2279 | ### 生成重载 2280 | 2281 | 通常,如果你写一个有默认参数值的 Kotlin 方法,在 Java 里,只会有一个有完整参数的签名。如果你要暴露多个重载给java调用者,你可以使用 @JvmOverloads 注解。 2282 | 2283 | 构造函数,静态函数等也能用这个标记。但他不能用在抽象方法上,包括 接口中的方法。 2284 | 2285 | 注意一下,Secondary Constructors 描述过,如果一个类的所有构造函数参数都有默认 值,会生成一个公开的无参构造函数。这就算 没有@JvmOverloads 注解也有效。 2286 | 2287 | ### Null安全性 2288 | 2289 | 当从 Java 中调用 Kotlin 函数时,没人阻止我们传递 null 给一个非空参数。 这就是为什么 Kotlin 给所有期望非空参数的公开函数生成运行时检测。 这样我们就能在 Java 代码里立即得到 NullPointerException。 2290 | 2291 | ### Nothing 类型的转换 2292 | 2293 | Nothing 是一种特殊的类型,因为它在 Java 中没有类型相对应。事实上,每个 Java 的引用类型,包括 java.lang.Void 都可以接受 null值,但是 Nothing 不行,因此在 Java 世界中没有什么可以代表这个类型,这就是为什么在Kotlin 中要生成原始类型需要使用 Nothing。 2294 | 2295 | ------ 2296 | -------------------------------------------------------------------------------- /notes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcxiaoke/kotlin-notes/e5dd4490b20b91d326bb8691e2a9e6e03b8d5510/notes.pdf -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/Basics.kt: -------------------------------------------------------------------------------- 1 | package com.mcxiaoke.kotlin 2 | 3 | /** 4 | * User: mcxiaoke 5 | * Date: 2016/1/20 6 | * Time: 21:17 7 | */ 8 | 9 | // 基础类型 10 | 11 | // 数字 12 | // 与Java类似,但是不完全一样 13 | // Kotlin提供下列内置类型 14 | /** 15 | Type Bit width 16 | Double 64 17 | Float 32 18 | Long 64 19 | Int 32 20 | Short 16 21 | Byte 8 22 | * 23 | */ 24 | 25 | // 字面量 26 | /** 27 | 十进制: 123 28 | Long类型 以L结尾: 123L 29 | 十六进制: 0x0F 30 | 二进制: 0b00001011 31 | 注意:不支持八进制. 32 | 33 | 也支持下列类型的浮点数: 34 | 35 | 默认是Double: 123.5, 123.5e10 36 | Float类型以F或f结尾: 123.5f 37 | * 38 | */ 39 | 40 | // 表现方式 41 | 42 | fun box1() { 43 | val a: Int = 10000 44 | print(a === a) // Prints 'true' 45 | val boxedA: Int? = a 46 | val anotherBoxedA: Int? = a 47 | print(boxedA === anotherBoxedA) // !!!Prints 'false'!!! 48 | } 49 | 50 | fun box2() { 51 | val a: Int = 10000 52 | print(a == a) // Prints 'true' 53 | val boxedA: Int? = a 54 | val anotherBoxedA: Int? = a 55 | print(boxedA == anotherBoxedA) // Prints 'true' 56 | } 57 | 58 | // 显式转换 59 | // 小数类型不是大数类型的子类型,所以下面的代码编译不过 60 | fun conversation1() { 61 | // Hypothetical code, does not actually compile: 62 | val a: Int? = 1 // A boxed Int (java.lang.Integer) 63 | // val b: Long? = a // implicit conversion yields a boxed Long (java.lang.Long) 64 | // print(a == b) // Surprise! This prints "false" as Long's equals() check for other part to be Long as well 65 | val b: Byte = 1 // OK, literals are checked statically 66 | // val i: Int = b // ERROR 67 | val i: Int = b.toInt() // OK: explicitly widened 68 | } 69 | 70 | // 所有的数字类型都支持下列转换 71 | /** 72 | oByte(): Byte 73 | toShort(): Short 74 | toInt(): Int 75 | toLong(): Long 76 | toFloat(): Float 77 | toDouble(): Double 78 | toChar(): Char 79 | **/ 80 | 81 | // 隐式类型转换很少见 82 | fun implicit() { 83 | val l = 1L + 3 // Long + Int => Long 84 | } 85 | 86 | // 操作符 87 | //Kotlin支持标准的算术操作符 88 | // 还支持位操作 89 | fun bitwise() { 90 | val x = (1 shl 2) and 0x000FF000 91 | } 92 | 93 | /** 94 | shl(bits) – signed shift left (Java’s <<) 95 | shr(bits) – signed shift right (Java’s >>) 96 | ushr(bits) – unsigned shift right (Java’s >>>) 97 | and(bits) – bitwise and 98 | or(bits) – bitwise or 99 | xor(bits) – bitwise xor 100 | inv() – bitwise inversion 101 | **/ 102 | 103 | // 字符 104 | // 字符是Char类型,不能当作数字,使用单引号包含 105 | fun testChar(c: Char) { 106 | // if (c == 1) { // ERROR: incompatible types 107 | // ... 108 | // } 109 | } 110 | 111 | // 布尔值 112 | // 两个值:true和false, &&和||和!三种操作 113 | 114 | // 数组 115 | // Kotlin的数组是Array类型,有get和set方法[],size属性 116 | /** 117 | class Array private constructor() { 118 | val size: Int 119 | fun get(index: Int): T 120 | fun set(index: Int, value: T): Unit 121 | 122 | fun iterator(): Iterator 123 | // ... 124 | } 125 | **/ 126 | 127 | // 创建数组 128 | fun testArray1() { 129 | val asc = Array(5, { i -> (i * i).toString() }) 130 | } 131 | 132 | // Kotlin的数组是不可变的 133 | // 不允许将Array赋值给Array 134 | // Kotlin还有ByteArray, ShortArray, IntArray等类型 135 | fun testArray2() { 136 | val x: IntArray = intArrayOf(1, 2, 3) 137 | x[0] = x[1] + x[2] 138 | } 139 | 140 | // 字符串 String 141 | // 字符串是不可变的,可以通过[i]访问单个字符 142 | fun testString1(str: String) { 143 | for (c in str) { 144 | println(c) 145 | } 146 | } 147 | 148 | // 字符串字面量 149 | fun testString2() { 150 | val s = "Hello, world!\n" 151 | // 或者三引号 152 | val text = """ 153 | for (c in "foo") 154 | print(c) 155 | """ 156 | } 157 | 158 | // 字符串模板 159 | fun stringTemplate() { 160 | val i = 10 161 | val s = "i = $i" // evaluates to "i = 10" 162 | val x = "abc" 163 | val str = "$x.length is ${x.length}" // evaluates to "abc.length is 3" 164 | // 可以包含反义字符 165 | val price = "${'$'}9.99" 166 | } 167 | 168 | 169 | // 包 170 | // package和Java类似,import也类似 171 | /** 172 | import foo.Bar // Bar is now accessible without qualification 173 | import foo.* // everything in 'foo' becomes accessible 174 | **/ 175 | 176 | // 还可以使用as关键词解决命名冲突 177 | /** 178 | import foo.Bar // Bar is accessible 179 | import bar.Bar as bBar // bBar stands for 'bar.Bar' 180 | **/ 181 | 182 | // import还可以用于导入顶层函数和属性,可以导入对象内的函数和属性,导入enum常量 183 | // 顶层声明的默认可见性是private 184 | 185 | 186 | // 控制流 187 | 188 | // if表达式 189 | fun ifExp1(a: Int, b: Int) { 190 | // Traditional usage 191 | var max1 = a 192 | if (a < b) 193 | max1 = b 194 | 195 | // With else 196 | var max2: Int 197 | if (a > b) 198 | max2 = a 199 | else 200 | max2 = b 201 | 202 | // As expression 203 | val max3 = if (a > b) a else b 204 | } 205 | 206 | // 分支里最后一个表达式的值是返回值 207 | fun ifExp2(a: Int, b: Int) { 208 | val max = if (a > b) { 209 | print("Choose a") 210 | a 211 | } else { 212 | print("Choose b") 213 | b 214 | } 215 | } 216 | 217 | fun Int.isOdd(): Boolean { 218 | return this % 2 != 0 219 | } 220 | 221 | fun Int.isEven(): Boolean { 222 | return this % 2 == 0 223 | } 224 | 225 | // when表达式 226 | // 匹配所有流程直到匹配上 227 | fun whenExp(x: Int) { 228 | when (x) { 229 | 1 -> print("x == 1") 230 | 2 -> print("x == 2") 231 | else -> { 232 | // Note the block 233 | print("x is neither 1 nor 2") 234 | } 235 | } 236 | 237 | // 还可以使用逗号 238 | when (x) { 239 | 0, 1 -> print("x == 0 or x == 1") 240 | else -> print("otherwise") 241 | } 242 | 243 | // 还可以使用表达式 244 | val s = "123" 245 | when (x) { 246 | parseInt(s) -> print("s encodes x") 247 | else -> print("s does not encode x") 248 | } 249 | 250 | // 使用range和collection 251 | val validNumbers = intArrayOf(1, 2, 3, 4, 100) 252 | when (x) { 253 | in 1..10 -> print("x is in the range") 254 | in validNumbers -> print("x is valid") 255 | !in 10..20 -> print("x is outside the range") 256 | else -> print("none of the above") 257 | } 258 | 259 | // 使用is检查值 260 | val hasPrefix = when (s) { 261 | is String -> s.startsWith("prefix") 262 | else -> false 263 | } 264 | 265 | // 代替if-else if 266 | val n = 100 267 | when { 268 | n.isOdd() -> print("x is odd") 269 | n.isEven() -> print("x is even") 270 | else -> print("x is funny") 271 | } 272 | } 273 | 274 | // for循环 275 | fun forExp1(items: Collection) { 276 | for (item in items) 277 | print(item) 278 | } 279 | 280 | // 或者这样 281 | fun forExp2(items: Array) { 282 | for (i in items.indices) 283 | print(items[i]) 284 | } 285 | 286 | // 使用with 287 | fun forExp3(array: Array) { 288 | for ((index, value) in array.withIndex()) { 289 | println("the element at $index is $value") 290 | } 291 | } 292 | 293 | // while循环 294 | fun whileExp(x: Int) { 295 | while (x > 0) { 296 | // x-- 297 | } 298 | 299 | do { 300 | val y: String? = "String" 301 | // val y = retrieveData() 302 | } while (y != null) // y is visible here! 303 | } 304 | 305 | // Kotlin也支持传统的break和continue 306 | 307 | 308 | // 返回值和跳转 309 | /** 310 | 三种跳转操作 311 | return. 从最里层的函数或者匿名函数返回 312 | break. 结束最里层的循环. 313 | continue. 跳到最里层的循环的下一步. 314 | **/ 315 | 316 | // 返回和跳转标志 317 | // break label 318 | fun jumpExp1() { 319 | loop@ for (i in 1..100) { 320 | for (j in 1..100) { 321 | if (i > 50) 322 | break@loop 323 | } 324 | } 325 | } 326 | 327 | // return label 328 | // Kotlin支持函数闭包 329 | // 从lambda表达式返回 330 | fun jumpExp2(ints: Array) { 331 | fun foo() { 332 | ints.forEach { 333 | if (it == 0) return 334 | print(it) 335 | } 336 | } 337 | } 338 | 339 | // 从闭包函数返回 340 | fun jumpExp3(ints: Array) { 341 | ints.forEach lit@ { 342 | if (it == 0) return@lit 343 | print(it) 344 | } 345 | } 346 | 347 | // 从闭包函数返回,也可以这样用 348 | fun jumpExp4(ints: Array) { 349 | ints.forEach { 350 | if (it == 0) return@forEach 351 | print(it) 352 | } 353 | } 354 | 355 | // 或者使用匿名函数,而不是lambda表达式 356 | fun jumpExp5(ints: Array) { 357 | ints.forEach(fun(value: Int) { 358 | if (value == 0) return 359 | print(value) 360 | }) 361 | } 362 | 363 | 364 | 365 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/ClassesAndInheritance.kt: -------------------------------------------------------------------------------- 1 | package com.mcxiaoke.kotlin 2 | 3 | import kotlin.reflect.jvm.internal.impl.javax.inject.Inject 4 | 5 | /** 6 | * User: mcxiaoke 7 | * Date: 2016/1/20 8 | * Time: 22:03 9 | */ 10 | // 类使用class关键字声明 11 | class Invoice {} 12 | 13 | // 大括号也可以忽略 14 | class Empty 15 | 16 | // 构造函数 17 | // Kotlin的泪可以有一个主要构造器和一个或多个次要构造器 18 | // 主要构造器放在类名字后面 19 | class Person1 constructor(firstName: String) {} 20 | 21 | // 如果主要构造器没有注解或者可见性修饰符,constructor关键字可以省略 22 | class Person2(firstName: String) 23 | 24 | // 主要构造器不能包含代码块,初始化代码块使用init关键字 25 | class Customer2(name: String) { 26 | init { 27 | println("Customer initialized with value $name") 28 | } 29 | } 30 | 31 | // 初始化块中可以使用主要构造器的参数 32 | // 初始化块中也可以用于属性初始化 33 | class Customer3(name: String) { 34 | val customerKey = name.toUpperCase() 35 | } 36 | 37 | // 在主要构造器中声明并初始化属性的语法糖 38 | class Person3(val firstName: String, 39 | val lastName: String, 40 | var age: Int) { 41 | // ... 42 | } 43 | 44 | // 如果主要构造器有注解或可见性修饰符,constructor就不能省略 45 | class Customer4 public @Inject constructor(name: String) { //... 46 | // 47 | } 48 | 49 | // 类也可以声明次要构造器,前缀constructor 50 | class Person4 { 51 | constructor(parent: Person1) { 52 | // do something 53 | // parent.children.add(this) 54 | } 55 | } 56 | 57 | // 如果类有主要构造器,次要构造器必须调用主要构造器 58 | class Person5(val name: String) { 59 | constructor(name: String, parent: Person1) : this(name) { 60 | // do something 61 | // parent.children.add(this) 62 | } 63 | } 64 | 65 | // 如果非抽象类并且没有任何构造器,会生成一个无参的public默认构造器 66 | // 如果想改变这个行为,可以这样 67 | class DoNotCreateMe private constructor() { 68 | } 69 | 70 | // 创建类的实例 71 | // Kotlin没有new关键字 72 | val invoice = Invoice() 73 | val customer = Customer2("Joe Smith") 74 | 75 | // 类成员 76 | // 类可以包含构造器和初始化块,函数,属性,内部类,对象声明 77 | 78 | // 继承 79 | // Kotlin中所有的类的公共基类是Any,也是没有父类的类的默认父类 80 | class Example // 隐式继承自Any 81 | // Any不是java.lang.Object,没有equals/hashCode/toString之外的成员 82 | 83 | // 使用冒号声明显式继承 84 | open class Base(p: Int) 85 | class Derived(p: Int) : Base(p) 86 | 87 | // 如果一个类有主要构造器,子类必须初始化父类的构造器参数 88 | // 次要构造器使用super关键字初始化父类 89 | /** 90 | class MyView : View { 91 | constructor(ctx: Context) : super(ctx) { 92 | } 93 | 94 | constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) { 95 | } 96 | } 97 | **/ 98 | 99 | // class默认是final,除非使用open关键字 100 | 101 | // 覆写类成员 102 | // 需要显式声明,open和override都是必须的 103 | // 使用override关键字的函数默认是open的,可以被子类覆盖 104 | open class Base1 { 105 | open fun v() { 106 | } 107 | 108 | fun nv() { 109 | } 110 | } 111 | 112 | class Derived1() : Base1() { 113 | override fun v() { 114 | } 115 | } 116 | 117 | // 覆写规则 118 | // 如果一个类从父类继承链继承了某个成员的多个实现,就必须覆写此成员,并提供自己的实现 119 | // 可以调用父类的实现,示例如下 120 | open class A { 121 | open fun f() { print("A") } 122 | fun a() { print("a") } 123 | } 124 | 125 | interface B { 126 | fun f() { print("B") } // interface members are 'open' by default 127 | fun b() { print("b") } 128 | } 129 | 130 | class C() : A(), B { 131 | // The compiler requires f() to be overridden: 132 | // 必须覆写函数f() 133 | override fun f() { 134 | super.f() // call to A.f() 135 | super.f() // call to B.f() 136 | } 137 | } 138 | 139 | // 抽象类 140 | // 类和类的某些成员可以生命为abstract,这样的类必须是open的 141 | open class Base2 { 142 | open fun f() {} 143 | } 144 | 145 | abstract class Derived2 : Base2() { 146 | override abstract fun f() 147 | } 148 | 149 | // 同伴对象 Companion Objects 150 | // Kotlin的类没有static方法,可以使用包级函数代替 151 | // 如果一定要类方法,可以使用同伴对象 152 | class MyClass { 153 | companion object Factory { 154 | fun create(): MyClass = MyClass() 155 | } 156 | } 157 | val instance = MyClass.create() 158 | 159 | // 密封类 Sealed Classes 160 | // 密封类使用sealed修饰符,放在class前面,可以有内部子类 161 | // 类似于Enum 162 | // 密封类的子类不一定是内部类,可以放在任何地方 163 | sealed class Expr { 164 | class Const(val number: Double) : Expr() 165 | class Sum(val e1: Expr, val e2: Expr) : Expr() 166 | object NotANumber : Expr() 167 | } 168 | 169 | // 密封类和when表达式搭配使用 170 | fun eval(expr: Expr): Double = when(expr) { 171 | is Expr.Const -> expr.number 172 | is Expr.Sum -> eval(expr.e1) + eval(expr.e2) 173 | Expr.NotANumber -> Double.NaN 174 | // the `else` clause is not required because we've covered all the cases 175 | } 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/DataClasses.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * User: mcxiaoke 3 | * Date: 16/1/21 4 | * Time: 11:06 5 | */ 6 | 7 | // 数据类 Data Classes 8 | // 数据类只用于存储数据,使用data关键字 9 | data class User1(val name: String, val age: Int) 10 | // 按照主要构造器中定义的属性 11 | // 编译器自动给数据类增加equals/hashCode/toString/copy()方法 12 | // 如果某个成员在类内部定义了,就不会自动生成 13 | // 数据类必须满足下列条件: 14 | // 1. 主要构造器至少有一个参数 15 | // 2. 所有的主要构造器参数必须使用var或val 16 | // 3. 数据类不能是抽象类,封闭类,内部类 17 | // 4. 数据类不能继承其它类(可以实现接口) 18 | 19 | // 数据类如果想要生成一个无参构造器,必须为所有的参数指定默认值 20 | data class User2(val name: String = "", val age: Int = 0) 21 | 22 | // 复制 23 | // 生成的copy函数用于复制某些属性 24 | 25 | class User3(val name: String, val age: Int) { 26 | fun copy(name: String = this.name, age: Int = this.age) = User1(name, age) 27 | } 28 | 29 | // 可以这样使用 30 | fun dc1() { 31 | val jack = User3(name = "Jack", age = 1) 32 | val olderJack = jack.copy(age = 2) 33 | } 34 | 35 | // 分支函数 36 | fun dc2() { 37 | val jane = User3("Jane", 35) 38 | // val (name, age) = jane 39 | // println("$name, $age years of age") // prints "Jane, 35 years of age" 40 | } 41 | 42 | // 数据类和析构声明见析构章节 43 | 44 | // Pair和Triple是标准库提供的两个数据类 45 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/Delegation.kt: -------------------------------------------------------------------------------- 1 | import kotlin.properties.Delegates 2 | import kotlin.reflect.KProperty 3 | 4 | /** 5 | * User: mcxiaoke 6 | * Date: 16/1/21 7 | * Time: 12:14 8 | */ 9 | 10 | // 委托 Delegation 11 | 12 | // 类委托 Class Delegation 13 | // 代理模式是继承的很好的替代品,Kotlin支持委托 14 | interface Base { 15 | fun print() 16 | } 17 | 18 | class BaseImpl(val x: Int) : Base { 19 | override fun print() { 20 | print(x) 21 | } 22 | } 23 | 24 | // by关键字表示将所有Base的方法委托给b 25 | class Derived(b: Base) : Base by b 26 | 27 | fun delegate1() { 28 | val b = BaseImpl(10) 29 | Derived(b).print() // prints 10 30 | } 31 | 32 | // 委托属性 33 | // 有很多公共类型的属性不需要每次都实现,可以放入一个库中: 34 | // 1. 延迟属性:在第一次访问时才计算值 35 | // 2. 可观察属性:属性值变化时观察者获得通知 36 | // 3. 存储在map中的属性,不是单独的字段 37 | 38 | // 委托属性的语法是:val/var : by 39 | // 委托对象需要有 getValue/setValue方法 40 | 41 | class Delegate { 42 | operator fun getValue(thisRef: Any?, property: KProperty<*>): String { 43 | return "$thisRef, thank you for delegating '${property.name}' to me!" 44 | } 45 | 46 | operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { 47 | println("$value has been assigned to '${property.name} in $thisRef.'") 48 | } 49 | } 50 | 51 | class Example { 52 | var p: String by Delegate() 53 | } 54 | 55 | fun delegate2() { 56 | val e = Example() 57 | e.p = "NewValue" 58 | println(e.p) 59 | } 60 | 61 | // 属性委托条件 62 | /** 63 | 1. 对于只读属性,委托对象必须有一个getValue()方法,参数: 64 | receiver - 必须是属性所有者同类型或者父类型 65 | metadata - 必须是KProperty<*>及其父类型 66 | 67 | 2. 对于读写属性,委托对象还必须有一个setValue()方法,参数: 68 | receiver - 要求同getValue 69 | metadata - 要求同getValue 70 | new value - 必须是属性的同类型或者父类型 71 | 72 | 3. getValue/setValue可以是成员函数,也可以是扩展函数 73 | 74 | **/ 75 | 76 | // Kotlin提供了一些标准类型的委托 77 | 78 | // 延迟委托 Lazy 79 | // 默认情况下,延迟属性的执行是同步的:值的计算在一个线程,其它所有线程看到相同的值 80 | // 多个线程初始化用LazyThreadSafetyMode.PUBLICATION参数 81 | // 如果不需要线程安全保证,用LazyThreadSafetyMode.NONE 82 | 83 | val lazyValue: String by lazy { 84 | println("computed!") 85 | "Hello" 86 | } 87 | 88 | fun lazy1() { 89 | println(lazyValue) 90 | println(lazyValue) 91 | } 92 | 93 | // 可观察对象 Observable 94 | // Delegates.observable()接受两个参数,初始值和修改handler 95 | // 每次修改属性值时(操作完成后),handler会被调用 96 | // handler有三个参数:property, old value, new value 97 | 98 | class User { 99 | // 另有一个vetoable在值被修改之前调用 100 | var name: String by Delegates.observable("") { 101 | prop, old, new -> 102 | println("$old -> $new") 103 | } 104 | } 105 | 106 | // out: 107 | // -> first 108 | // first -> second 109 | fun observable1() { 110 | val user = User() 111 | user.name = "first" 112 | user.name = "second" 113 | } 114 | 115 | // 存储在Map中的属性 116 | // 在Map中存储属性是一个很常见的用例,例如解析JSON或者其它动态数据 117 | 118 | class User4(val map: Map) { 119 | val name: String by map 120 | val age: Int by map 121 | } 122 | 123 | fun map1() { 124 | val user = User4(mapOf( 125 | "name" to "John Doe", 126 | "age" to 25 127 | )) 128 | println(user.name) // Prints "John Doe" 129 | println(user.age) // Prints 25 130 | } 131 | 132 | // 可变属性使用MutableMap 133 | class MutableUser(val map: MutableMap) { 134 | var name: String by map 135 | var age: Int by map 136 | } 137 | 138 | 139 | fun main(args: Array) { 140 | map1() 141 | } 142 | 143 | 144 | 145 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/Extensions.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * User: mcxiaoke 3 | * Date: 16/1/21 4 | * Time: 10:48 5 | */ 6 | 7 | // 扩展 Extensions 8 | // 类似于C#,Kotlin的扩展提供了扩展一个已有类的能力 9 | // Kotlin支持扩展函数和扩展属性 10 | 11 | // 扩展函数 12 | // 函数的名字要使用被扩展的类型的名字做前缀 13 | // 下面的代码给MutableList类型增加了swap方法 14 | // this引用被扩展类型的接受对象 15 | fun MutableList.swap(index1: Int, index2: Int) { 16 | val tmp = this[index1] // 'this'表示这个列表 17 | this[index1] = this[index2] 18 | this[index2] = tmp 19 | } 20 | 21 | fun ext1() { 22 | val l = arrayListOf(1, 2, 3) 23 | l.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'l' 24 | } 25 | 26 | // 还可以让这个函数更通用一点 27 | fun MutableList.swap2(index1: Int, index2: Int) { 28 | val tmp = this[index1] // 'this'表示这个列表 29 | this[index1] = this[index2] 30 | this[index2] = tmp 31 | } 32 | 33 | // 扩展是静态解析和分发的 34 | // resolved and dispatched statically 35 | // 扩展并没有真正修改它扩展的类,扩展不能给类增加新的成员 36 | // 这意味着扩展函数是按照表达式的类型调用,而不是按照运行时表达式的结果的类型来调用 37 | // 如下例子 38 | open class CC 39 | 40 | class DD : CC() 41 | 42 | fun CC.foo() = "c" 43 | 44 | fun DD.foo() = "d" 45 | 46 | fun printFoo(c: CC) { 47 | println(c.foo()) 48 | } 49 | 50 | fun ext2() { 51 | // printFoo的参数类型是CC,所以打印出来的是c 52 | printFoo(DD()) // print 'c' 53 | } 54 | 55 | // 如果这个类有相同签名的成员函数,成员函数总是胜出 56 | class CCC { 57 | fun foo() { 58 | println("member") 59 | } 60 | } 61 | 62 | fun CCC.foo() { 63 | println("extension") 64 | } 65 | 66 | fun ext3() { 67 | var c = CCC() 68 | c.foo() // print ‘member’ 69 | } 70 | 71 | // 可空接受者 72 | // 可以定义nullable的接受者扩展 73 | fun Any?.toString(): String { 74 | if (this == null) return "null" 75 | // after the null check, 'this' is auto cast to a non-null type, 76 | // so the toString() below 77 | // resolves to the member function of the Any class 78 | return toString() 79 | } 80 | 81 | // 扩展属性 82 | val List.lastIndex: Int 83 | get() = size - 1 84 | 85 | // 同伴对象的扩展 86 | class MyClass { 87 | companion object {} // will be called "Companion" 88 | } 89 | 90 | fun MyClass.Companion.foo() { 91 | // ... 92 | } 93 | 94 | fun ext4() { 95 | MyClass.foo() 96 | } 97 | 98 | // 扩展的作用域 99 | // 使用import,详情参考import章节 100 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/FunctionsAndLambdas.kt: -------------------------------------------------------------------------------- 1 | import java.util.* 2 | import java.util.concurrent.locks.Lock 3 | import javax.swing.tree.TreeNode 4 | 5 | /** 6 | * User: mcxiaoke 7 | * Date: 16/1/21 8 | * Time: 13:24 9 | */ 10 | 11 | // 函数 Functions 12 | 13 | // 函数声明使用fun关键字 14 | fun double(x: Int): Int { 15 | return x * 2 16 | } 17 | 18 | val result = double(2) 19 | 20 | // Define extension to Int 21 | infix fun Int.mul(x: Int): Int { 22 | return this * x 23 | } 24 | 25 | // 函数调用支持中缀表达式 26 | fun infix() { 27 | 4 mul 2 28 | 4.mul(2) 29 | } 30 | 31 | // 函数参数 32 | fun powerOf(number: Int, exponent: Int) { 33 | println("power function") 34 | } 35 | 36 | // 默认参数 37 | fun read(b: Array, off: Int = 0, len: Int = b.size) { 38 | // do something 39 | } 40 | 41 | // 命名参数 42 | fun reformat(str: String, 43 | normalizeCase: Boolean = true, 44 | upperCaseFirstLetter: Boolean = true, 45 | divideByCamelHumps: Boolean = false, 46 | wordSeparator: Char = ' ') { 47 | // function body 48 | } 49 | 50 | // 注意:调用Java方法时不能使用命名参数 51 | fun nameArgs() { 52 | var str = "Named Args Test" 53 | reformat(str) 54 | reformat(str, true, true, false, '_') 55 | reformat(str, 56 | normalizeCase = true, 57 | upperCaseFirstLetter = true, 58 | divideByCamelHumps = false, 59 | wordSeparator = '_' 60 | ) 61 | reformat(str, wordSeparator = '_') 62 | } 63 | 64 | // 无返回值的函数可以省略Unit 65 | fun printHello(name: String?): Unit { 66 | if (name != null) 67 | println("Hello $name") 68 | else 69 | println("Hi there!") 70 | // `return Unit` or `return` is optional 71 | } 72 | 73 | // 单行表达式函数可以省略大括号 74 | fun double1(x: Int): Int = x * 2 75 | 76 | fun double2(x: Int) = x * 2 77 | 78 | // Kotlin不会自动推断有函数体的函数的返回值 79 | 80 | // 可变数目参数使用vararg 81 | fun asList(vararg ts: T): List { 82 | val result = ArrayList() 83 | for (t in ts) // ts is an Array 84 | result.add(t) 85 | return result 86 | } 87 | 88 | // 如果可变参数不是列表的最后一个 89 | // 之后的参数可以使用命名参数语法 90 | fun varargs() { 91 | val list = asList(1, 2, 3) 92 | } 93 | 94 | // 还可以使用展开操作符* 95 | fun spread() { 96 | val a = arrayOf(1, 2, 3) 97 | val list = asList(-1, 0, *a, 4) 98 | println(list) // [-1,0,1,2,3,4] 99 | } 100 | 101 | // 函数作用域 102 | // Kotlin可以在顶级作用域声明函数,不需要创建一个类 103 | 104 | // 局部函数 (其它函数内部的函数) 105 | class Graph { 106 | var vertices: Array = arrayOf() 107 | } 108 | 109 | class Vertex { 110 | var neighbors: Set = setOf() 111 | } 112 | 113 | fun dfs1(graph: Graph) { 114 | fun dfs(current: Vertex, visited: MutableSet) { 115 | if (!visited.add(current)) return 116 | for (v in current.neighbors) 117 | dfs(v, visited) 118 | } 119 | 120 | dfs(graph.vertices[0], HashSet()) 121 | } 122 | 123 | // 局部函数可以访问外层函数的局部变量 124 | fun dfs2(graph: Graph) { 125 | val visited = HashSet() 126 | fun dfs(current: Vertex) { 127 | if (!visited.add(current)) return 128 | for (v in current.neighbors) 129 | dfs(v) 130 | } 131 | 132 | dfs(graph.vertices[0]) 133 | } 134 | 135 | // 局部函数甚至可以从外层函数返回 136 | fun reachable(from: Vertex, to: Vertex): Boolean { 137 | val visited = HashSet() 138 | fun dfs(current: Vertex) { 139 | // here we return from the outer function: 140 | // not allowed in v1.0.0-beta-4584 141 | // if (current == to) return@reachable true 142 | // And here -- from local function: 143 | if (!visited.add(current)) return 144 | for (v in current.neighbors) 145 | dfs(v) 146 | } 147 | 148 | dfs(from) 149 | return false // if dfs() did not return true already 150 | } 151 | 152 | // 成员函数 153 | // 类或对象内部的函数称作成员函数 154 | class Sample() { 155 | fun foo() { 156 | print("Foo") 157 | } 158 | } 159 | 160 | fun members1() { 161 | Sample().foo() 162 | } 163 | 164 | // 泛型函数 165 | fun singletonList2(item: T): List { 166 | // ... 167 | return listOf() 168 | } 169 | 170 | // 内联函数和扩展函数见相关章节 171 | 172 | // 尾递归函数 173 | // 使用tailrec关键字,函数的最后一个操作必须是调用自身 174 | tailrec fun findFixPoint(x: Double = 1.0): Double 175 | = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x)) 176 | 177 | // 对应的传统方式定义 178 | private fun findFixPoint(): Double { 179 | var x = 1.0 180 | while (true) { 181 | val y = Math.cos(x) 182 | if (x == y) return y 183 | x = y 184 | } 185 | } 186 | 187 | 188 | // Lambda表达式 189 | 190 | // 高阶函数是指接受函数作为参数,或者以函数作为返回值的函数 191 | // body的类型是一个函数: () -> T 192 | fun lock(lock: Lock, body: () -> T): T { 193 | lock.lock() 194 | try { 195 | return body() 196 | } finally { 197 | lock.unlock() 198 | } 199 | } 200 | 201 | // lambda表达式 202 | /** 203 | * 1. lambda表达式用大括号包裹 204 | * 2. lambda表达式的参数在 -> 之前声明 205 | * 3. lambda表示打的body在 -> 之后出现 206 | */ 207 | 208 | // Kotlin中,如果最后一个参数是函数,这个参数可以放在括号的后面 209 | fun List.map1(transform: (T) -> R): List { 210 | val result = arrayListOf() 211 | for (item in this) 212 | result.add(transform(item)) 213 | return result 214 | } 215 | 216 | fun fun2() { 217 | val ints: Collection = listOf(1, 2, 3, 4, 5, 6, 7, 8) 218 | val doubled = ints.map { it -> it * 2 } 219 | println(doubled) 220 | } 221 | 222 | // 为方便,如果一个函数仅有一个参数,可以省略声明,参数名为it 223 | fun fun3() { 224 | val ints: Collection = listOf(1, 2, 3, 4, 5, 6, 7, 8) 225 | val doubled = ints.map { it * 2 } 226 | println(doubled) 227 | val strings: Collection = listOf("Hello", "World", "Cat", "Rabbit") 228 | // LINQ风格的表达式 229 | // strings.filter { it.length == 5 }.sortBy { it }.map { it.toUpperCase() } 230 | } 231 | 232 | // Lambda表达式和匿名函数 233 | // lambda和匿名函数都是函数字面量 234 | fun max(strings: Collection, comp: (String, String) -> Boolean) { 235 | } 236 | 237 | fun compare(a: String, b: String): Boolean = a.length < b.length 238 | 239 | fun fun4() { 240 | val strings: Collection = listOf("Hello", "World", "Cat", "Rabbit") 241 | max(strings, { a, b -> a.length < b.length }) 242 | } 243 | 244 | // 函数类型 245 | // less的类型是 (T, T) -> Boolean 246 | // val compare: (x: T, y: T) -> Int = ... 247 | fun max2(collection: Collection, less: (T, T) -> Boolean): T? { 248 | var max: T? = null 249 | for (it in collection) 250 | if (max == null || less(max, it)) 251 | max = it 252 | return max 253 | } 254 | 255 | // lambda表达式语法 256 | val sum1 = { x: Int, y: Int -> x + y } 257 | 258 | // 不省略参数声明的话是这样的 259 | val sum2: (Int, Int) -> Int = { x, y -> x + y } 260 | 261 | // 匿名函数 262 | // 匿名函数和普通的函数差不多,但是没有名字 263 | //fun(x: Int, y: Int): Int { 264 | // return x + y 265 | //} 266 | fun fun5() { 267 | val ints: Collection = listOf(-1, -2, -3, 0, 1, 2) 268 | // 匿名函数,参数不能省略,返回值自动推导 269 | ints.filter(fun(item) = item > 0) 270 | // lambda表达式,参数可以省略 271 | ints.filter { it > 0 } 272 | } 273 | 274 | // 闭包 Closures 275 | // lambda表达式和匿名函数可以访问它的闭包 276 | fun fun6() { 277 | val ints: Collection = listOf(-1, -2, -3, 0, 1, 2) 278 | var sum = 0 279 | ints.filter { it > 0 }.forEach { 280 | sum += it 281 | } 282 | print(sum) 283 | } 284 | 285 | //有Receiver的函数字面量 286 | fun fun7() { 287 | val sum3 = fun Int.(other: Int): Int = this + other 288 | 3.sum3(4) 289 | 5.sum3(100) 290 | } 291 | 292 | // lambda表达式的隐式Receiver 293 | private class HTML { 294 | fun body() { 295 | /****/ 296 | } 297 | } 298 | 299 | private fun html(init: HTML.() -> Unit): HTML { 300 | val html = HTML() // create the receiver object 301 | html.init() // pass the receiver object to the lambda 302 | return html 303 | } 304 | 305 | // 可用于创建DSL 306 | fun fun8() { 307 | html { // lambda with receiver begins here 308 | body() // calling a method on the receiver object 309 | } 310 | } 311 | 312 | // 内联函数 313 | // 使用高阶函数是由代价的,运行时每个函数都是一个对象,而且它捕获了闭包变量 314 | // 内存分配和许方法调用增加了运行时成本 315 | // 内联函数生成代码时直接插入函数和lambda表达式的代码 316 | // inline修饰符可以作用于函数和lambda表达式 317 | // inline让生成的代码量增加了 318 | //inline fun lock2(lock: Lock, body: () -> T): T { } 319 | // 可以同时使用noinline,如果你只想让某些lambda或函数内联 320 | 321 | // 非局部返回 322 | // 使用return只能从命名的普通函数或匿名函数返回 323 | // 要想从lambda返回,需要使用一个标志 324 | fun ordinaryFunction() { 325 | } 326 | 327 | inline fun inlineFunction() { 328 | } 329 | 330 | //fun foo1() { 331 | // ordinaryFunction { 332 | // // return // ERROR: can not make `foo` return here 333 | // } 334 | //} 335 | // 336 | //fun foo2() { 337 | // inlineFunction { 338 | // return // OK: the lambda is inlined 339 | // } 340 | //} 341 | 342 | // 位于lambda表达式里,但是从闭包函数返回成为非局部返回 343 | // 这种形式行用于循环 344 | fun hasZeros(ints: List): Boolean { 345 | ints.forEach { 346 | if (it == 0) return true // returns from hasZeros 347 | } 348 | return false 349 | } 350 | 351 | // 内联的lambda目前比支持break和continue 352 | inline fun f(crossinline body: () -> Unit) { 353 | val f1 = Runnable { body() } 354 | // ... 355 | } 356 | 357 | // 具体化类型参数 358 | // 又是我们需要访问传递给函数的参数的类型 359 | fun TreeNode.findParentOfType1(clazz: Class): T? { 360 | var p = parent 361 | while (p != null && !clazz.isInstance(p)) { 362 | p = p.parent 363 | } 364 | @Suppress("UNCHECKED_CAST") 365 | return p as T 366 | } 367 | 368 | abstract class MyTreeNodeType : TreeNode {} 369 | // 调用方法 370 | // myTree.findParentOfType2(MyTreeNodeType::class.java) 371 | 372 | // 使用reified修饰符后,T就可以在内部访问了,像一个普通的类一样 373 | // 现在可以这样调用 374 | // myTree.findParentOfType() 375 | inline fun TreeNode.findParentOfType(): T? { 376 | var p = parent 377 | while (p != null && p !is T) { 378 | p = p?.parent 379 | } 380 | return p as T 381 | } 382 | 383 | // 很多时候不需要反射,也可以用reified 384 | inline fun membersOf() = T::class.members 385 | 386 | fun main(s: Array) { 387 | println(membersOf().joinToString("\n")) 388 | } 389 | 390 | // 普通函数没有reified参数,inline函数才可以 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/Generics.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * User: mcxiaoke 3 | * Date: 16/1/21 4 | * Time: 11:15 5 | */ 6 | 7 | // 泛型 8 | // Kotlin的类也可以有泛型参数,例如 9 | class Box(t: T) { 10 | var value = t 11 | } 12 | 13 | // 创建实例 14 | val box1: Box = Box(1) 15 | // 类型自动推导 16 | // 1是Int类型,所以编译器知道我们用的是Box 17 | val box2 = Box(1) 18 | 19 | // 与Java的不同 20 | // Java的泛型实现存在诸多限制 21 | /** 22 | // Java 23 | List strs = new ArrayList(); 24 | List objs = strs; // !!! The cause of the upcoming problem sits here. Java prohibits this! 25 | objs.add(1); // Here we put an Integer into a list of Strings 26 | String s = strs.get(0); // !!! ClassCastException: Cannot cast Integer to String 27 | 28 | // Java 29 | void copyAll(Collection to, Collection from) { 30 | to.addAll(from); // !!! Would not compile with the naive declaration of addAll: 31 | // Collection is not a subtype of Collection 32 | } 33 | */ 34 | 35 | // Java 36 | /** 37 | interface Source { 38 | T nextT(); 39 | } 40 | 41 | void demo(Source strs) { 42 | Source objects = strs; // !!! Not allowed in Java 43 | // ... 44 | } 45 | **/ 46 | 47 | // Java是使用位置变量 48 | // Kotlin是声明位置变量 49 | // Declaration-site variance 50 | abstract class Source { 51 | abstract fun nextT(): T 52 | } 53 | 54 | fun demo1(strs: Source) { 55 | // This is OK, since T is an out-parameter 56 | val objects: Source = strs 57 | // ... 58 | } 59 | 60 | // out被称作型变注释,协变 外边界 61 | // in被称作逆变注释,逆变 内边界 62 | abstract class Comparable1 { 63 | abstract fun compareTo(other: T): Int 64 | } 65 | 66 | fun demo2(x: Comparable1) { 67 | x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number 68 | // Thus, we can assign x to a variable of type Comparable 69 | val y: Comparable1 = x // OK! 70 | } 71 | 72 | // 类型预测 Type projections 73 | class Array1(val size: Int) { 74 | // fun get(index: Int): T { 75 | // /* ... */ 76 | // } 77 | 78 | fun set(index: Int, value: T) { 79 | /* ... */ 80 | } 81 | } 82 | 83 | fun copy1(from: Array, to: Array) { 84 | assert(from.size == to.size) 85 | for (i in from.indices) 86 | to[i] = from[i] 87 | } 88 | 89 | fun test1() { 90 | val ints: Array = arrayOf(1, 2, 3) 91 | // val any = Array(3) 92 | // 两个参数的类型必须一致 93 | // copy(ints, any) // Error: expects (Array, Array) 94 | } 95 | 96 | // Array对应Java中的Array 97 | fun copy2(from: Array, to: Array) { 98 | // ... 99 | } 100 | 101 | // Array对应Java中的Array 102 | fun fill(dest: Array, value: String) { 103 | // ... 104 | } 105 | 106 | // 泛型函数 107 | fun singletonList(item: T): List? { 108 | // ... 109 | return null 110 | } 111 | 112 | fun T.basicToString(): String? { 113 | // extension function 114 | // ... 115 | return null 116 | } 117 | 118 | val l = singletonList(1) 119 | 120 | // 泛型约束 121 | // 上边界 upper bounds 122 | fun > sort(list: List) { 123 | // ... 124 | } 125 | 126 | fun test2() { 127 | sort(listOf(1, 2, 3)) // 没问题. Int是Comparable的子类型 128 | // sort(listOf(HashMap())) 129 | // 错误: HashMap不是Comparable>的子类型 130 | } 131 | 132 | // 如果需要多个边界约束,可以用where语句 133 | fun stringsWhenGreater(list: List, threshold: T): List 134 | where T : Comparable, T : Cloneable { 135 | return list.filter { it > threshold }.map { it.toString() } 136 | } -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/GettingStarted.kt: -------------------------------------------------------------------------------- 1 | package com.mcxiaoke.kotlin 2 | 3 | import java.io.File 4 | 5 | /** 6 | * Author: mcxiaoke 7 | * Date: 2016/1/20 20:05 8 | */ 9 | 10 | // http://kotlinlang.org/docs/reference/basic-syntax.html 11 | 12 | // 定义函数 13 | fun sum1(a: Int, b: Int): Int { 14 | return a + b 15 | } 16 | 17 | // 使用类型推导的函数 18 | fun sum2(a: Int, b: Int) = a + b 19 | 20 | // 无返回值的函数 21 | fun printSum1(a: Int, b: Int): Unit { 22 | print(a + b) 23 | } 24 | 25 | // Unit可以忽略 26 | fun printSum2(a: Int, b: Int) { 27 | print(a + b) 28 | } 29 | 30 | // 定义局部变量 31 | // 不可变 32 | fun localVariables() { 33 | val a: Int = 1 34 | val b = 1 35 | val c: Int 36 | c = 1 37 | } 38 | 39 | // 可变 40 | fun mutableVariables() { 41 | var x = 5 42 | x += 1 43 | } 44 | 45 | // 字符串模板 46 | fun strTemplate() { 47 | var args = arrayOf("Cat", "Dog", "Rabbit") 48 | print("Hello ${args[0]}") 49 | } 50 | 51 | // 条件表达式 52 | fun max1(a: Int, b: Int): Int { 53 | if (a > b) return a 54 | else return b 55 | } 56 | 57 | // 也可以这样写 58 | fun max2(a: Int, b: Int) = if (a > b) a else b 59 | 60 | // 空值 nullable 61 | // 如果str不是数字,返回null 62 | fun parseInt(str: String): Int? { 63 | return str.toInt() 64 | } 65 | 66 | // 使用返回nullable的函数 67 | fun testInt(args: Array) { 68 | if (args.size < 2) { 69 | print("Two integers expected") 70 | return 71 | } 72 | 73 | val x = parseInt(args[0]) // Int? 74 | val y = parseInt(args[1])//Int? 75 | 76 | // Using `x * y` yields error because they may hold nulls. 77 | if (x != null && y != null) { 78 | // x and y are automatically cast to non-nullable after null check 79 | print(x * y) 80 | } 81 | } 82 | 83 | // 类型检查和自动转换 84 | fun getStringLength1(obj: Any): Int? { 85 | if (obj is String) { 86 | return obj.length 87 | } 88 | return null 89 | } 90 | 91 | // 也可以这样写 92 | fun getStringLength2(obj: Any): Int? { 93 | if (obj is String && obj.length > 0) { 94 | return obj.length 95 | } 96 | return null 97 | } 98 | 99 | // for循环 100 | fun forLoop1(args: Array) { 101 | for (arg in args) { 102 | print(arg) 103 | } 104 | } 105 | 106 | // 或者这样 107 | fun forLoop2(args: Array) { 108 | for (i in args.indices) { 109 | print(args[i]) 110 | } 111 | } 112 | 113 | // while循环 114 | fun whileLoop1(args: Array) { 115 | var i = 0 116 | while (i < args.size) 117 | print(args[i++]) 118 | } 119 | 120 | // when表达式 121 | fun cases(obj: Any) { 122 | when (obj) { 123 | 1 -> print("One") 124 | "Hello" -> print("Greeting") 125 | is Long -> print("Long") 126 | !is String -> print("Not a string") 127 | else -> print("Unknown") 128 | } 129 | } 130 | 131 | // 区间表达式 132 | fun range1(x: Int, y: Int) { 133 | if (x in 1..y - 1) { 134 | print("OK") 135 | } 136 | } 137 | 138 | fun range2(x: Int, array: Array) { 139 | if (x !in 0..array.lastIndex) { 140 | print("Out") 141 | } 142 | } 143 | 144 | fun range3(x: Int) { 145 | if (x in 1..5) { 146 | print(x) 147 | } 148 | } 149 | 150 | // 集合类型 151 | fun names1(names: Array) { 152 | for (name in names) { 153 | println(name) 154 | } 155 | } 156 | 157 | // 检查是否包含 158 | fun names2(text: String, names: Array) { 159 | if (text in names) { 160 | print("Yes") 161 | } 162 | } 163 | 164 | // 使用lambda表达式 165 | fun names3(names: Collection) { 166 | names.filter { it.startsWith("A") } 167 | .sortedBy { it } 168 | .map { it.toUpperCase() } 169 | .forEach { print(it) } 170 | } 171 | 172 | // 创建数据类 POJO 173 | // 自动生成 getter/setter/equals/hashCode/toString/copy等 174 | data class Customer(val name: String, val email: String) 175 | 176 | //函数参数默认值 177 | fun foo(a: Int = 0, b: String = "") { 178 | // do something 179 | } 180 | 181 | // 列表过滤 182 | fun filters(list: Collection) { 183 | val positives1 = list.filter { x -> x > 0 } 184 | // 也可以这样 185 | val positives2 = list.filter { it > 0 } 186 | } 187 | 188 | // map遍历 189 | fun maps(map: Map) { 190 | for ((k, v) in map) { 191 | println("$k -> $v") 192 | } 193 | } 194 | 195 | // 只读列表 196 | fun readOnlyList() { 197 | val list = listOf("a", "b", "c") 198 | } 199 | 200 | // 只读map 201 | fun readOnlyMap() { 202 | val map1 = mapOf("a" to 1, "b" to 2, "c" to 3) 203 | println(map1["a"]) 204 | // map2["key"] = 123 205 | } 206 | 207 | // lazy属性 208 | class LazyDemo { 209 | // val p:String by lazy{ 210 | // // compute the string 211 | // } 212 | } 213 | 214 | // 函数扩展 215 | fun Int.biggerThanTen(): Boolean { 216 | return this > 10 217 | } 218 | 219 | // 扩展测试 220 | fun extensions() { 221 | 100.biggerThanTen() 222 | 5.biggerThanTen() 223 | } 224 | 225 | // 单例模式 226 | object Resource { 227 | val name = "Name" 228 | } 229 | 230 | //nullable用法 231 | fun testNullable() { 232 | val files = File("Test").listFiles() 233 | println(files?.size) 234 | println(files?.size ?: "empty") 235 | } 236 | 237 | // when表达式 238 | fun transform(color: String): Int { 239 | return when (color) { 240 | "Red" -> 0 241 | "Green" -> 1 242 | "Blue" -> 2 243 | else -> throw IllegalArgumentException("Invalid color param value") 244 | } 245 | } 246 | 247 | // try catch用法 248 | fun tryCatch() { 249 | val result = try { 250 | // do something 251 | } catch (e: ArithmeticException) { 252 | throw IllegalStateException(e) 253 | } 254 | 255 | // Working with result 256 | } 257 | 258 | // if表达式 259 | fun ifExp(param: Int) { 260 | val result = if (param == 1) { 261 | "one" 262 | } else if (param == 2) { 263 | "two" 264 | } else { 265 | "three" 266 | } 267 | } 268 | 269 | // with表达式 270 | class Turtle { 271 | fun penDown() { 272 | } 273 | 274 | fun penUp() { 275 | } 276 | 277 | fun turn(degrees: Double) { 278 | } 279 | 280 | fun forward(pixels: Double) { 281 | } 282 | } 283 | 284 | // with用法 285 | fun withExp() { 286 | val turtle = Turtle() 287 | with(turtle) { //draw a 100 pix square 288 | penDown() 289 | for (i in 1..4) { 290 | forward(100.0) 291 | turn(90.0) 292 | } 293 | penUp() 294 | } 295 | } 296 | 297 | 298 | // main函数 299 | fun main(args: Array) { 300 | } 301 | 302 | 303 | // 编码约定 304 | // 命名风格 305 | /** 306 | * 1. 使用驼峰不要使用下划线 307 | * 2. 类型名是用大写字母开头 308 | * 3. 方法和属性名小写开头 309 | * 4. 使用四个空格的缩进 310 | * 5. 公开方法应该有文档 311 | * 312 | */ 313 | 314 | // 冒号前后有空格 315 | 316 | // Lambdas 317 | // 大括号两边有空格,箭头两边有空格 318 | // list.filter { it > 10 }.map { element -> element * 2 } 319 | 320 | // Unit 321 | // 如果一个函数返回Unit,应该忽略 322 | fun noReturn(){} 323 | 324 | 325 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/InterfacesAndVisibilityModifiers.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * User: mcxiaoke 3 | * Date: 16/1/21 4 | * Time: 10:27 5 | */ 6 | // 接口 7 | // Kotlin中的接口和Java 8类似,可以包含抽象方法的声明和实现,但是不能存储状态 8 | // 可以包含属性,但是只能是抽象的,或者提供访问方法实现 9 | // 接口定义使用interface关键字 10 | interface MyInterface { 11 | fun bar() 12 | fun foo() { 13 | // optional body 14 | } 15 | } 16 | 17 | // 一个类或对象可实现一个或多个接口 18 | class Child : MyInterface { 19 | override fun bar() { 20 | // body 21 | } 22 | } 23 | 24 | // 接口的属性 25 | // 接口的属性是抽象的,否则必须提供访问访问 26 | // 接口的属性没有支持字段,不能使用field访问 27 | interface MyInterface2 { 28 | val property: Int // abstract 29 | 30 | val propertyWithImplementation: String 31 | get() = "foo" 32 | 33 | fun foo() { 34 | print(property) 35 | } 36 | } 37 | 38 | class Child2 : MyInterface2 { 39 | override val property: Int = 29 40 | } 41 | 42 | // 解决覆写冲突 43 | interface A { 44 | fun foo() { 45 | print("A") 46 | } 47 | 48 | fun bar() 49 | } 50 | 51 | interface B { 52 | fun foo() { 53 | print("B") 54 | } 55 | 56 | fun bar() { 57 | print("bar") 58 | } 59 | } 60 | 61 | class C : A { 62 | override fun bar() { 63 | print("bar") 64 | } 65 | } 66 | 67 | class D : A, B { 68 | override fun foo() { 69 | super.foo() 70 | super.foo() 71 | } 72 | 73 | override fun bar() { 74 | super.bar() 75 | } 76 | } 77 | 78 | 79 | // 可见性修饰符 80 | // 类,对象,接口,构造器,函数,属性和设置方法都可以有可见性修饰符 81 | // Kotlin有四种可见性修饰符:private, protected, internal, public 82 | // 如果没有修饰符,默认是public 83 | // 函数,属性,类,对象和接口可以在顶级(某个package里)声明 84 | // file name: example.kt 85 | fun baz() { 86 | } 87 | 88 | class Bar {} 89 | 90 | // 顶级声明如果没有任何标识符,默认是public,在任何地方都可见 91 | // 如果使用private,只能在包含它的文件里可见 92 | // 如果使用internal,只能在同一个模块可见 93 | // protected不能用于顶级声明 94 | // 举例 95 | // file name: example.kt 96 | 97 | private fun foo() { 98 | } // 只在当前文件可见 99 | 100 | public var bar: Int = 5 // 属性bar任何地方可见 101 | private set // 设置方法只在当前文件科技 102 | 103 | internal val baz = 6 // 只在同一模块可见 104 | 105 | // 类和接口中的可见性 106 | // private - 只在此类内部可见 107 | // protected - 在类和它的直接子类内部可见 108 | // internal - 模块的任何客户端都可见 109 | // pubic - 对任何能看到这个类的客户端可见 110 | // 注意: Kotlin外部类不能看到内部类的私有成员 111 | // 举例 112 | open class Outer { 113 | private val a = 1 114 | protected val b = 2 115 | internal val c = 3 116 | val d = 4 // public by default 117 | 118 | protected class Nested { 119 | public val e: Int = 5 120 | } 121 | } 122 | 123 | class Subclass : Outer() { 124 | // a 不可见 125 | // b, c 和 d 可见 126 | // Nested 和 e 可见 127 | } 128 | 129 | class Unrelated(o: Outer) { 130 | // o.a, o.b 不可见 131 | // o.c 和 o.d 可见 (同一个模块) 132 | // Outer.Nested 不可见, Nested::e 也不可见 133 | } 134 | 135 | // 构造器的可见性 136 | // 构造器默认是public的,和类的可见性一样 137 | class CR private constructor(a: Int) { 138 | /****/ 139 | } 140 | 141 | // 局部变量,函数和类不能使用可见性修饰符 142 | 143 | // 模块的可见性 144 | // internal表示只在同一个模块里可见 145 | // 模块表示编译在一起的一组Kotlin文件集合 146 | // 如一个IDEA模块,一个Maven/Gradle工程,一个ANT任务编译的文件集合 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/Interoperability.kt: -------------------------------------------------------------------------------- 1 | import java.util.* 2 | import java.util.concurrent.Executors 3 | 4 | /** 5 | * User: mcxiaoke 6 | * Date: 16/1/21 7 | * Time: 16:03 8 | */ 9 | 10 | // Java和Kotlin的互操作 11 | 12 | // Kotlin中调用Java代码 13 | // https://kotlinlang.org/docs/reference/java-interop.html 14 | 15 | // 所有的Java代码都可以在Kotlin中调用 16 | fun callJava1(source: List) { 17 | val list = ArrayList() 18 | // 'for'-loops work for Java collections: 19 | for (item in source) 20 | list.add(item) 21 | // Operator conventions work as well: 22 | for (i in 0..source.size - 1) 23 | list[i] = source[i] // get and set are called 24 | } 25 | 26 | // Getters和Setters自动转换为属性调用 27 | fun callJava2() { 28 | val calendar = Calendar.getInstance() 29 | if (calendar.firstDayOfWeek == Calendar.SUNDAY) { 30 | // call getFirstDayOfWeek() 31 | calendar.firstDayOfWeek = Calendar.MONDAY // call setFirstDayOfWeek() 32 | } 33 | } 34 | 35 | // Null安全 36 | fun callJava3() { 37 | val list = ArrayList() // 非空 (constructor result) 38 | list.add("Item") 39 | val size = list.size // 非空 (primitive int) 40 | val item = list[0] // platform type inferred (ordinary Java object) 41 | 42 | item.substring(1) // allowed, may throw an exception if item == null 43 | val nullable: String? = item // allowed, always works 44 | val notNull: String = item // allowed, may fail at runtime 45 | } 46 | 47 | // JDK类型注意 48 | /** 49 | T! 表示 “T or T?”, 50 | (Mutable)Collection! 表示 “Java collection of T may be mutable or not, may be nullable or not”, 51 | Array<(out) T>! 表示 “Java array of T (or a subtype of T), nullable or not” 52 | */ 53 | 54 | // Kotlin和Java类型映射关系 55 | /** 56 | 原始类型 57 | Java type Kotlin type 58 | byte kotlin.Byte 59 | short kotlin.Short 60 | int kotlin.Int 61 | long kotlin.Long 62 | char kotlin.Char 63 | float kotlin.Float 64 | double kotlin.Double 65 | boolean kotlin.Boolean 66 | 67 | Java type Kotlin type 68 | int[] kotlin.IntArray! 69 | String[] kotlin.Array<(out) String>! 70 | 71 | 引用类型 72 | Java type Kotlin type 73 | java.lang.Object kotlin.Any! 74 | java.lang.Cloneable kotlin.Cloneable! 75 | java.lang.Comparable kotlin.Comparable! 76 | java.lang.Enum kotlin.Enum! 77 | java.lang.Annotation kotlin.Annotation! 78 | java.lang.Deprecated kotlin.Deprecated! 79 | java.lang.Void kotlin.Nothing! 80 | java.lang.CharSequence kotlin.CharSequence! 81 | java.lang.String kotlin.String! 82 | java.lang.Number kotlin.Number! 83 | java.lang.Throwable kotlin.Throwable! 84 | 85 | 集合类型 86 | Java type Kotlin read-only type Kotlin mutable type Loaded platform type 87 | Iterator Iterator MutableIterator (Mutable)Iterator! 88 | Iterable Iterable MutableIterable (Mutable)Iterable! 89 | Collection Collection MutableCollection (Mutable)Collection! 90 | Set Set MutableSet (Mutable)Set! 91 | List List MutableList (Mutable)List! 92 | ListIterator ListIterator MutableListIterator (Mutable)ListIterator! 93 | Map Map MutableMap (Mutable)Map! 94 | Map.Entry Map.Entry MutableMap.MutableEntry (Mutable)Map.(Mutable)Entry! 95 | */ 96 | 97 | // Kotlin中Java的泛型 98 | // Kotlin的泛型也只是编译时的 99 | /** 100 | Foo 变为 Foo! 101 | Foo 变为 Foo! 102 | List 变为 List<*>!, i.e. List! 103 | */ 104 | 105 | // Java的数组 106 | class JavaArrayExample { 107 | 108 | fun removeIndices(indices: IntArray) { 109 | // code here... 110 | } 111 | } 112 | 113 | // 数组和可变参数列表 114 | fun javaArray1() { 115 | val javaObj = JavaArrayExample() 116 | val array = intArrayOf(0, 1, 2, 3) 117 | javaObj.removeIndices(array) // passes int[] to method 118 | 119 | val array2 = arrayOf(1, 2, 3, 4) 120 | array[x] = array[x] * 2 // no actual calls to get() and set() generated 121 | for (x in array) // no iterator created 122 | print(x) 123 | 124 | for (i in array2.indices) // no iterator created 125 | array[i] += 2 126 | } 127 | 128 | // 对象方法 129 | fun callJava4(x: Any) { 130 | (x as java.lang.Object).wait() 131 | val fooClass = x.javaClass 132 | val fooClass2 = Foo::class.java 133 | 134 | } 135 | 136 | class Example2 : Cloneable { 137 | override fun clone(): Any { 138 | /****/ 139 | return Example2() 140 | } 141 | 142 | protected fun finalize() { 143 | // finalization logic 144 | } 145 | } 146 | 147 | //SAM用法,类似于Java 8 148 | fun callJava5() { 149 | val runnable = Runnable { println("This runs in a runnable") } 150 | val executor = Executors.newCachedThreadPool() 151 | // Java signature: void execute(Runnable command) 152 | executor.execute { println("This runs in a thread pool") } 153 | executor.execute(Runnable { println("This runs in a thread pool") }) 154 | } 155 | 156 | 157 | // Java中调用Kotlin代码 158 | // https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html 159 | 160 | // 生成的Java类的名字可以用 @JvmName注解改变 161 | /** 162 | @file:JvmName("DemoUtils") 163 | package demo 164 | class Foo 165 | fun bar() { 166 | } 167 | **/ 168 | 169 | // 字段 170 | // 如果想将Kotlin的属性导出为Java的成员字段 171 | // 可以使用 @JvmField 注解 172 | /** 173 | class C(id: String) { 174 | @JvmField val ID = id 175 | } 176 | // Java 177 | class JavaClient { 178 | public String getID(C c) { 179 | return c.ID; 180 | } 181 | } 182 | */ 183 | 184 | // 静态方法和字段使用@JvmStatic 185 | /** 186 | class C { 187 | companion object { 188 | @JvmStatic fun foo() {} 189 | fun bar() {} 190 | } 191 | } 192 | 193 | C.foo(); // works fine 194 | C.bar(); // error: not a static method 195 | 196 | object Obj { 197 | @JvmStatic fun foo() {} 198 | fun bar() {} 199 | } 200 | 201 | Basic Syntax 202 | Idioms 203 | Coding Conventions 204 | Basics 205 | Classes and Objects 206 | Functions and Lambdas 207 | Functions 208 | Lambdas 209 | Inline Functions 210 | Other 211 | Destructuring Declarations 212 | Ranges 213 | Type Checks and Casts 214 | This expressions 215 | Equality 216 | Operator overloading 217 | Null Safety 218 | Exceptions 219 | Annotations 220 | Reflection 221 | Type-Safe Builders 222 | Dynamic Type 223 | Reference 224 | API Reference 225 | Grammar 226 | Interop 227 | Calling Java from Kotlin 228 | Calling Kotlin from Java 229 | Tools 230 | FAQ 231 | Full Kotlin Reference 232 | Edit Page 233 | Calling Kotlin from Java 234 | 235 | Kotlin code can be called from Java easily. 236 | 237 | Properties 238 | 239 | Property getters are turned into get-methods, and setters – into set-methods. 240 | 241 | Package-Level Functions 242 | 243 | All the functions and properties declared in a file example.kt inside a package org.foo.bar are put into a Java class named org.foo.bar.ExampleKt. 244 | 245 | // example.kt 246 | package demo 247 | 248 | class Foo 249 | 250 | fun bar() { 251 | } 252 | // Java 253 | new demo.Foo(); 254 | demo.ExampleKt.bar(); 255 | The name of the generated Java class can be changed using the @JvmName annotation: 256 | 257 | @file:JvmName("DemoUtils") 258 | 259 | package demo 260 | 261 | class Foo 262 | 263 | fun bar() { 264 | } 265 | // Java 266 | new demo.Foo(); 267 | demo.DemoUtils.bar(); 268 | Having multiple files which have the same generated Java class name (the same package and the same name or the same @JvmName annotation) is normally an error. However, the compiler has the ability to generate a single Java facade class which has the specified name and contains all the declarations from all the files which have that name. To enable the generation of such a facade, use the @JvmMultifileClass annotation in all of the files. 269 | 270 | // oldutils.kt 271 | @file:JvmName("Utils") 272 | @file:JvmMultifileClass 273 | 274 | package demo 275 | 276 | fun foo() { 277 | } 278 | // newutils.kt 279 | @file:JvmName("Utils") 280 | @file:JvmMultifileClass 281 | 282 | package demo 283 | 284 | fun bar() { 285 | } 286 | // Java 287 | demo.Utils.foo(); 288 | demo.Utils.bar(); 289 | Fields 290 | 291 | If you need to expose a Kotlin property as a field in Java, you need to annotate it with the @JvmField annotation. The field will have the same visibility as the underlying property. You can annotate a property with @JvmField if it has a backing field, is not private, does not have open, override or const modifiers, and is not a delegated property. 292 | 293 | class C(id: String) { 294 | @JvmField val ID = id 295 | } 296 | // Java 297 | class JavaClient { 298 | public String getID(C c) { 299 | return c.ID; 300 | } 301 | } 302 | Static Methods and Fields 303 | 304 | As mentioned above, Kotlin generates static methods for package-level functions. Kotlin can also generate static methods for functions defined in named objects or companion objects if you annotate those functions as @JvmStatic. For example: 305 | 306 | class C { 307 | companion object { 308 | @JvmStatic fun foo() {} 309 | fun bar() {} 310 | } 311 | } 312 | Now, foo() is static in Java, while bar() is not: 313 | 314 | C.foo(); // works fine 315 | C.bar(); // error: not a static method 316 | Same for named objects: 317 | 318 | object Obj { 319 | @JvmStatic fun foo() {} 320 | fun bar() {} 321 | } 322 | In Java: 323 | 324 | Obj.foo(); // works fine 325 | Obj.bar(); // error 326 | Obj.INSTANCE.bar(); // works, a call through the singleton instance 327 | Obj.INSTANCE.foo(); // works too 328 | 329 | object Obj { 330 | val CONST = 1 331 | } 332 | const val MAX = 239 333 | 334 | In Java: 335 | int c = Obj.CONST; 336 | int d = ExampleKt.MAX; 337 | */ 338 | 339 | // 使用@JvmName解决签名冲突 340 | /** 341 | 这里编译后类型擦除后会冲突 342 | fun List.filterValid(): List 343 | fun List.filterValid(): List 344 | 345 | 解决办法: 346 | fun List.filterValid(): List 347 | @JvmName("filterValidInt") 348 | fun List.filterValid(): List 349 | 350 | val x: Int 351 | @JvmName("getX_prop") 352 | get() = 15 353 | 354 | fun getX() = 10 355 | */ 356 | 357 | // 方法重载 358 | /** 359 | 360 | 根据参数默认值生成不同的方法 361 | @JvmOverloads fun f(a: String, b: Int = 0, c: String = "abc") { 362 | ... 363 | } 364 | 365 | // Java 366 | void f(String a, int b, String c) { } 367 | void f(String a, int b) { } 368 | void f(String a) { } 369 | */ 370 | 371 | 372 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/NestedClassesAndEnum.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * User: mcxiaoke 3 | * Date: 16/1/21 4 | * Time: 11:59 5 | */ 6 | 7 | // 内部类 8 | 9 | // Nested Classes 相当于Java的静态内部类 10 | class Outer2 { 11 | private val bar: Int = 1 12 | 13 | class Nested { 14 | fun foo() = 2 15 | } 16 | } 17 | 18 | val demo2 = Outer2.Nested().foo() // == 2 19 | 20 | // Inner classes 相当于Java的内部类 21 | class Outer3 { 22 | private val bar: Int = 1 23 | 24 | inner class Inner { 25 | fun foo() = bar 26 | } 27 | } 28 | 29 | val demo3 = Outer3().Inner().foo() // == 1 30 | 31 | // 枚举 Enum Classes 32 | enum class Direction { 33 | NORTH, SOUTH, WEST, EAST 34 | } 35 | 36 | // 枚举初始化 37 | enum class Color(val rgb: Int) { 38 | RED(0xFF0000), 39 | GREEN(0x00FF00), 40 | BLUE(0x0000FF) 41 | } 42 | 43 | // 枚举的匿名类 44 | enum class ProtocolState { 45 | WAITING { 46 | override fun signal() = TALKING 47 | }, 48 | 49 | TALKING { 50 | override fun signal() = WAITING 51 | }; 52 | 53 | abstract fun signal(): ProtocolState 54 | } 55 | 56 | // 枚举常量 57 | fun enum1() { 58 | var color = Color.valueOf("0xFF0000") 59 | var colors = Color.values() 60 | var name = Color.GREEN.name 61 | var ord = Color.RED.ordinal 62 | } -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/Objects.kt: -------------------------------------------------------------------------------- 1 | import java.awt.event.MouseAdapter 2 | import java.awt.event.MouseEvent 3 | 4 | /** 5 | * User: mcxiaoke 6 | * Date: 16/1/21 7 | * Time: 12:04 8 | */ 9 | 10 | // 对象表达式和声明 11 | 12 | // 对象表达式 13 | /** 14 | window.addMouseListener(object : MouseAdapter() { 15 | override fun mouseClicked(e: MouseEvent) { 16 | // ... 17 | } 18 | 19 | override fun mouseEntered(e: MouseEvent) { 20 | // ... 21 | } 22 | }) 23 | **/ 24 | 25 | // 如果父类有构造器,必须初始化父类 26 | // 使用逗号分隔多个父类 27 | open class AA(x: Int) { 28 | public open val y: Int = x 29 | } 30 | 31 | interface BB { 32 | /****/ 33 | } 34 | 35 | fun obj1() { 36 | val ab = object : AA(1), BB { 37 | override val y = 15 38 | } 39 | } 40 | 41 | // 如果需要一个没有任何父类的对象 42 | fun obj2() { 43 | val adHoc = object { 44 | var x: Int = 0 45 | var y: Int = 0 46 | } 47 | print(adHoc.x + adHoc.y) 48 | } 49 | 50 | // 类似于Java的匿名内部类,可以访问闭包的变量 51 | /** 52 | fun countClicks(window: JComponent) { 53 | var clickCount = 0 54 | var enterCount = 0 55 | 56 | window.addMouseListener(object : MouseAdapter() { 57 | override fun mouseClicked(e: MouseEvent) { 58 | clickCount++ 59 | } 60 | 61 | override fun mouseEntered(e: MouseEvent) { 62 | enterCount++ 63 | } 64 | }) 65 | // ... 66 | } 67 | */ 68 | 69 | // 对象声明使用object关键字 70 | // Object declarations 71 | // 声明一个单例 72 | class DataProvider 73 | 74 | object DataProviderManager { 75 | fun registerDataProvider(provider: DataProvider) { 76 | // ... 77 | } 78 | 79 | val allDataProviders: Collection 80 | get() = listOf()// ... 81 | } 82 | 83 | // 也可以这样 84 | object DefaultListener : MouseAdapter() { 85 | override fun mouseClicked(e: MouseEvent) { 86 | // ... 87 | } 88 | 89 | override fun mouseEntered(e: MouseEvent) { 90 | // ... 91 | } 92 | } 93 | 94 | // 同伴对象 Companion Objects 95 | // 使用companion关键字 96 | // 相当于Java中的static方法 97 | class MyClass2 { 98 | companion object Factory { 99 | fun create(): MyClass = MyClass() 100 | } 101 | } 102 | 103 | val instance = MyClass2.create() 104 | 105 | // 同伴对象可以实现接口 106 | interface Factory { 107 | fun create(): T 108 | } 109 | 110 | 111 | class MyClass3 { 112 | companion object : Factory { 113 | override fun create(): MyClass3 = MyClass3() 114 | } 115 | } 116 | 117 | // 在JVM上,同伴对象会生成真正的static方法和字段 118 | // 有关@JvmStatic注解参考Java互操作章节 119 | 120 | // 对象表达式和对象声明的区别 121 | // 1. 对象声明是第一次访问时延迟初始化的 122 | // 2. 对象表达式是使用时立即执行的 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/Others.kt: -------------------------------------------------------------------------------- 1 | import com.mcxiaoke.kotlin.parseInt 2 | import org.w3c.dom.Node 3 | import kotlin.reflect.jvm.javaField 4 | import kotlin.reflect.jvm.javaGetter 5 | 6 | /** 7 | * User: mcxiaoke 8 | * Date: 16/1/21 9 | * Time: 14:58 10 | */ 11 | 12 | // 解构声明 Destructuring Declarations 13 | // 有时候需要将一个对象解构为几个变量,例如 14 | // val (name, age) = person 15 | // 解构声明相当于下面的代码 16 | // val name = person.component1() 17 | // val age = person.component2() 18 | // 解构声明在for循环中也可以使用 19 | // for ((a, b) in collection) { ... } 20 | 21 | // 示例,从一个函数返回两个值 22 | // Data Class 数据类自动生成componentN()函数 23 | 24 | class Status 25 | 26 | data class Person(val name: String, val age: Int) 27 | data class Result(val result: Int, val status: Status) 28 | 29 | fun function(): Result { 30 | // computations 31 | return Result(result, Status()) 32 | } 33 | 34 | fun destruct1() { 35 | val (result, status) = function() 36 | val person = Person("Cat", 5) 37 | val (name, age) = person 38 | } 39 | 40 | // 示例: 解构声明和Map 41 | fun destruct2(map: Map) { 42 | for ((key, value) in map) { 43 | // do something with the key and the value 44 | } 45 | } 46 | 47 | /** 48 | 标准库提供的方法 49 | operator fun Map.iterator(): Iterator> = entrySet().iterator() 50 | operator fun Map.Entry.component1() = getKey() 51 | operator fun Map.Entry.component2() = getValue() 52 | **/ 53 | 54 | // 区间 Range 55 | // ClosedRange 56 | 57 | fun range1(i: Int) { 58 | if (i in 1..10) { 59 | // equivalent of 1 <= i && i <= 10 60 | println(i) 61 | } 62 | for (j in 1..4) print(j) // prints "1234" 63 | for (j in 4..1) print(j) // prints nothing 64 | for (j in 4 downTo 1) print(j) // prints "4321" 65 | } 66 | 67 | // Range rangeTo() 68 | /** 69 | class Int { 70 | //... 71 | operator fun rangeTo(other: Long): LongRange = LongRange(this, other) 72 | //... 73 | operator fun rangeTo(other: Int): IntRange = IntRange(this, other) 74 | //... 75 | } 76 | **/ 77 | 78 | // Range downTo() 79 | fun Long.downTo(other: Int): LongProgression { 80 | return LongProgression.fromClosedRange(this, other as Long, -1) 81 | } 82 | 83 | fun Byte.downTo(other: Int): IntProgression { 84 | return IntProgression.fromClosedRange(this as Int, other, -1) 85 | } 86 | 87 | // Range reversed() 88 | fun IntProgression.reversed(): IntProgression { 89 | return IntProgression.fromClosedRange(last, first, -step) 90 | } 91 | 92 | // Range step() 93 | fun IntProgression.step(step: Int): IntProgression { 94 | if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step") 95 | return IntProgression.fromClosedRange(first, last, if (this.step > 0) step else -step) 96 | } 97 | 98 | fun CharProgression.step(step: Int): CharProgression { 99 | if (step <= 0) throw IllegalArgumentException("Step must be positive, was: $step") 100 | return CharProgression.fromClosedRange(first, last, step) 101 | } 102 | 103 | fun range2() { 104 | (1..12 step 2).last == 11 // progression with values [1, 3, 5, 7, 9, 11] 105 | (1..12 step 3).last == 10 // progression with values [1, 4, 7, 10] 106 | (1..12 step 4).last == 9 // progression with values [1, 5, 9] 107 | } 108 | 109 | // 类型检查和强制转换 Type Checks and Casts 110 | 111 | // is和!is操作符 112 | // 用于运行时判断对象的类型 113 | fun typeCheck(obj: Any) { 114 | if (obj is String) { 115 | print(obj.length) 116 | } 117 | 118 | if (obj !is String) { 119 | // same as !(obj is String) 120 | print("Not a String") 121 | } else { 122 | print(obj.length) 123 | } 124 | } 125 | 126 | // 智能类型转换 127 | // if判断之后就知道类型了 128 | fun cast1(x: Any) { 129 | if (x is String) { 130 | print(x.length) // x is automatically cast to String 131 | } 132 | } 133 | 134 | fun cast2(x: Any) { 135 | // x is automatically cast to string on the right-hand side of `||` 136 | if (x !is String || x.length == 0) return 137 | 138 | // x is automatically cast to string on the right-hand side of `&&` 139 | if (x is String && x.length > 0) 140 | print(x.length) // x is automatically cast to String 141 | } 142 | 143 | fun cast3(x: Any) { 144 | when (x) { 145 | is Int -> print(x + 1) 146 | is String -> print(x.length + 1) 147 | is IntArray -> print(x.sum()) 148 | } 149 | } 150 | 151 | // 智能类型检查和转换的规则: 152 | /** 153 | * 1. val局部变量 - 总是转换 154 | * 2. val属性 - 适用于同一个模块的private或internal属性 155 | * 3. var局部变量 - 如果在检查和使用之间没有修改,没有比捕获的lambda修改 156 | * 4. var属性 - 不适用 157 | */ 158 | 159 | 160 | // 使用as操作符 161 | fun cast4(y: Any) { 162 | // 不安全的类型转换 163 | val x: String = y as String 164 | //"安全"的类型转换 165 | val x2: String? = y as? String 166 | } 167 | 168 | // This表达式 169 | // 对于类的成员,this指向当前类的对象 170 | // 对于扩展函数或函数字面量,this指向接受者参数指向的对象,即圆点的左侧 171 | // 如果没有限制,this指向最里层的作用域闭包 172 | 173 | // this限制 174 | class AO { // implicit label @A 175 | inner class B { // implicit label @B 176 | fun Int.foo() { 177 | // implicit label @foo 178 | val a = this@AO // A's this 179 | val b = this@B // B's this 180 | 181 | val c = this // foo()'s receiver, an Int 182 | val c1 = this@foo // foo()'s receiver, an Int 183 | 184 | // val funLit = @lambda {String.() -> 185 | // val d = this // funLit's receiver 186 | // val d1 = this@lambda // funLit's receiver 187 | // } 188 | 189 | // val funLit2 = { (s: String) -> 190 | // // foo()'s receiver, since enclosing lambda expression 191 | // // doesn't have any receiver 192 | // val d1 = this 193 | // } 194 | } 195 | } 196 | } 197 | 198 | // 相等性 Equality 199 | // 检查两个引用是否是同一个用三引号 === 200 | // 检查内容是否相同用双引号 == 201 | // a === b 等价于 a?.equals(b) ?: (b === null) 202 | 203 | // 操作符重载 Operator overloading 204 | 205 | // 一元操作符 206 | /** 207 | Expression Translated to 208 | +a a.unaryPlus() 209 | -a a.unaryMinus() 210 | !a a.not() 211 | 212 | Expression Translated to 213 | a++ a.inc() + see below 214 | a-- a.dec() + see below 215 | */ 216 | 217 | // 二元操作符 218 | /** 219 | Binary operations 220 | 221 | Expression Translated to 222 | a + b a.plus(b) 223 | a - b a.minus(b) 224 | a * b a.times(b) 225 | a / b a.div(b) 226 | a % b a.mod(b) 227 | a..b a.rangeTo(b) 228 | 229 | Expression Translated to 230 | a in b b.contains(a) 231 | a !in b !b.contains(a) 232 | 233 | For in and !in the procedure is the same, but the order of arguments is reversed. 234 | 235 | Symbol Translated to 236 | a[i] a.get(i) 237 | a[i, j] a.get(i, j) 238 | a[i_1, ..., i_n] a.get(i_1, ..., i_n) 239 | a[i] = b a.set(i, b) 240 | a[i, j] = b a.set(i, j, b) 241 | a[i_1, ..., i_n] = b a.set(i_1, ..., i_n, b) 242 | 243 | Symbol Translated to 244 | a() a.invoke() 245 | a(i) a.invoke(i) 246 | a(i, j) a.invoke(i, j) 247 | a(i_1, ..., i_n) a.invoke(i_1, ..., i_n) 248 | 249 | Expression Translated to 250 | a += b a.plusAssign(b) 251 | a -= b a.minusAssign(b) 252 | a *= b a.timesAssign(b) 253 | a /= b a.divAssign(b) 254 | a %= b a.modAssign(b) 255 | 256 | Symbol Translated to 257 | a > b a.compareTo(b) > 0 258 | a < b a.compareTo(b) < 0 259 | a >= b a.compareTo(b) >= 0 260 | a <= b a.compareTo(b) <= 0 261 | */ 262 | 263 | 264 | // Null安全 Null Safety 265 | // Kotlin的类型系统目的在于消除空指针引用 266 | 267 | // ?: 操作符 268 | fun null1(b: String?) { 269 | var a: String = "abc" 270 | // a = null // compilation error 271 | val l = a.length 272 | // val l = b.length // error: variable 'b' can be null 273 | val l2 = if (b != null) b.length else -1 274 | 275 | if (b != null && b.length > 0) 276 | print("String of length ${b.length}") 277 | else 278 | print("Empty string") 279 | 280 | // safe call 281 | b?.length 282 | 283 | val l3: Int = if (b != null) b.length else -1 284 | // 等价于 285 | val l4 = b?.length ?: -1 286 | 287 | // 强制解包nullable 288 | // 可能会造成空指针异常 289 | val l5 = b!!.length 290 | } 291 | 292 | fun null2(v: Any) { 293 | 294 | // 安全类型转换 295 | val aInt: Int? = v as? Int 296 | } 297 | 298 | fun foo(node: Node): String? { 299 | val parent = node.parentNode ?: return null 300 | val name = node.nodeName ?: throw IllegalArgumentException("name expected") 301 | return name 302 | } 303 | 304 | // 异常 Exceptions 305 | // Kotlin中所有的异常都继承自Throwable 306 | class SomeException(message: String?) : Exception(message) { 307 | 308 | } 309 | 310 | fun error1() { 311 | try { 312 | // some code 313 | } catch (e: SomeException) { 314 | // handler 315 | } finally { 316 | // optional finally block 317 | } 318 | } 319 | 320 | // try是一个表达式,有返回值 321 | fun try1(input: String) { 322 | val a: Int? = try { 323 | parseInt(input) 324 | } catch (e: NumberFormatException) { 325 | null 326 | } 327 | } 328 | 329 | // 注解 Annotations 330 | 331 | // 注解声明 332 | @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, 333 | AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION, 334 | AnnotationTarget.CONSTRUCTOR, AnnotationTarget.PROPERTY_SETTER, 335 | AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY) 336 | @Retention(AnnotationRetention.SOURCE) 337 | @MustBeDocumented 338 | public annotation class Fancy 339 | 340 | // 注解使用 341 | @Fancy class Foo { 342 | @Fancy fun baz(@Fancy foo: Int): Int { 343 | return (@Fancy 1) 344 | } 345 | } 346 | 347 | // 如果想给主要构造器加注解,不能省略constructor关键字 348 | class Foo2 @Fancy constructor(dependency: String) { 349 | var x: String? = null 350 | @Fancy set 351 | } 352 | 353 | // 注解也可以有构造器 354 | // 构造器允许的参数类型: 355 | // 原始类型,字符串,类,Enum,其它注解,上述类型的数组 356 | annotation class Special(val why: String) 357 | 358 | @Special("example") class Foo3 {} 359 | 360 | // 注解作为其它注解的参数时,不需要@符号 361 | public annotation class ReplaceWith(val expression: String) 362 | 363 | public annotation class Deprecated( 364 | val message: String, 365 | val replaceWith: ReplaceWith = ReplaceWith("")) 366 | 367 | @Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other")) 368 | class Foo4 {} 369 | 370 | // 注解也可以用于lambda表达式 371 | annotation class SuspendAble 372 | 373 | val fa = @SuspendAble { Thread.sleep(10) } 374 | 375 | // 注解使用位置 Annotation Use-site Targets 376 | annotation class Ann 377 | 378 | class AnnotationExample(@field:Ann val foo: Int, // annotate Java field 379 | @get:Ann val bar: String, // annotate Java getter 380 | @param:Ann val quux: Double) // annotate Java constructor parameter{ 381 | // } 382 | 383 | // Kotlin百分百兼容Java的注解 384 | // 注意:实际上不是,比如ButterKnife使用就有问题 385 | 386 | 387 | // 反射 Reflection 388 | 389 | // 类 390 | fun reflection1() { 391 | val kc = MyClass::class // KClass 392 | val c = MyClass::class.java // java class 393 | } 394 | 395 | fun isOdd(x: Int) = x % 2 != 0 396 | 397 | // 函数引用 398 | fun reflection2() { 399 | 400 | val numbers = listOf(1, 2, 3) 401 | // ::isOdd 是函数类型 (Int) -> Boolean 的值 402 | println(numbers.filter(::isOdd)) // prints [1, 3] 403 | } 404 | 405 | // 函数组合 406 | // compose(f, g) = f(g(*)) 407 | fun compose(f: (B) -> C, g: (A) -> B): (A) -> C { 408 | return { x -> f(g(x)) } 409 | } 410 | 411 | fun compose1() { 412 | fun length(s: String) = s.length 413 | 414 | val oddLength = compose(::isOdd, ::length) 415 | val strings = listOf("a", "ab", "abc") 416 | 417 | println(strings.filter(oddLength)) // Prints "[a, abc]" 418 | } 419 | 420 | // 属性引用可以使用::操作符 421 | var x = 1 422 | 423 | fun propRef1() { 424 | // ::x KProperty 425 | println(::x.get()) // prints "1" 426 | ::x.set(2) 427 | println(x) // prints "2" 428 | 429 | val strs = listOf("a", "bc", "def") 430 | println(strs.map(String::length)) // prints [1, 2, 3] 431 | } 432 | 433 | //引用类的属性 434 | class ABC(val p: Int) 435 | 436 | fun propRef2() { 437 | val prop = ABC::p 438 | println(prop.get(ABC(1))) // prints "1" 439 | } 440 | 441 | // 扩展的属性引用 442 | val String.lastChar: Char 443 | get() = this[length - 1] 444 | 445 | fun propRef3() { 446 | println(String::lastChar.get("abc")) // prints "c" 447 | } 448 | 449 | // 与Java反射的互操作 450 | class ACC(val p: Int) 451 | 452 | fun javaReflect1() { 453 | println(ACC::p.javaGetter) // prints "public final int A.getP()" 454 | println(ACC::p.javaField) // prints "private final int A.p" 455 | } 456 | 457 | // 构造器引用 458 | class Foo5 459 | 460 | fun function(factory: () -> Foo5) { 461 | val x: Foo5 = factory() 462 | } 463 | 464 | fun reflection3() { 465 | function(::Foo5) 466 | } 467 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/PropertiesAndFields.kt: -------------------------------------------------------------------------------- 1 | package com.mcxiaoke.kotlin 2 | 3 | import java.util.* 4 | 5 | /** 6 | * User: mcxiaoke 7 | * Date: 2016/1/20 8 | * Time: 22:54 9 | */ 10 | 11 | // Kotlin的类支持属性 12 | // 不变属性用val,可变属性用var 13 | public class Address { 14 | public var name: String = "" 15 | public var street: String = "" 16 | public var city: String = "" 17 | public var state: String? = "" 18 | public var zip: String = "" 19 | } 20 | 21 | // 属性使用方法,就像Java中的字段一样 22 | fun copyAddress(address: Address): Address { 23 | val result = Address() // there's no 'new' keyword in Kotlin 24 | result.name = address.name // accessors are called 25 | result.street = address.street 26 | // ... 27 | return result 28 | } 29 | 30 | // Getters和Setters 31 | /** 32 | var : [= ] 33 | [] 34 | [] 35 | */ 36 | // 初始化块,设置方法和读取方法都是可选的,属性类型也是可选的 37 | class Example2 { 38 | // var allByDefault: Int? 39 | // error: explicit initializer required, default getter and setter implied 40 | var initialized = 1 // has type Int, default getter and setter 41 | 42 | // 只读属性 43 | // val simple: Int? // has type Int, default getter, must be initialized in constructor 44 | val inferredType = 1 // has type Int and a default getter 45 | 46 | var size = 0 47 | 48 | fun setDataFromString(value: String) { 49 | // set value 50 | } 51 | 52 | // 自定义访问方法 53 | val isEmpty: Boolean 54 | get() = this.size == 0 55 | 56 | // 自定义设置方法 57 | var stringRepresentation: String 58 | get() = this.toString() 59 | // 设置方法默认参数是value,可以选择其它的名字 60 | set(value) { 61 | // parses the string and assigns values to other properties 62 | setDataFromString(value) 63 | } 64 | 65 | // 可以改变访问方法的可见性和修饰符 66 | var setterVisibility: String = "abc" // Initializer required, not a nullable type 67 | private set // the setter is private and has the default implementation 68 | 69 | // var setterWithAnnotation: Any? 70 | // @Inject set // annotate the setter with Inject 71 | } 72 | 73 | // 支持字段 74 | 75 | // Kotlin的类有属性,没有成员字段,但是可以使用field访问支持的字段 76 | class Example3 { 77 | // field只能在属性的访问方法中使用 78 | var counter = 0 // the initializer value is written directly to the backing field 79 | set(value) { 80 | if (value >= 0) 81 | field = value 82 | } 83 | 84 | var size = 100 85 | 86 | // 也可能没有field 87 | val isEmpty: Boolean 88 | get() = this.size == 0 89 | } 90 | 91 | // 支持属性 92 | class Example4 { 93 | private var _table: Map? = null 94 | public val table: Map 95 | get() { 96 | if (_table == null) 97 | _table = HashMap() // Type parameters are inferred 98 | return _table ?: throw AssertionError("Set to null by another thread") 99 | } 100 | } 101 | 102 | // 编译时常量 103 | // const常量属性:顶层或者object的成员,使用基础类型或String初始化,没有自定义访问方法 104 | const val SUBSYSTEM_DEPRECATED: String = "This subsystem is deprecated" 105 | 106 | @Deprecated(SUBSYSTEM_DEPRECATED) fun foo() { 107 | /** do something **/ 108 | } 109 | 110 | // 延迟初始化属性 111 | // 通常,声明为非null的属性必须在构造器中初始化。然而这样有时候不方便 112 | // 延迟初始化属性使用 lateinit 声明 113 | // 使用var在类内部声明,不能再主要构造器中,不能有自定义的访问和设置方法 114 | // 延迟初始化属性不能是null,也不能是原始类型 115 | /** 116 | public class LazyProperty { 117 | lateinit var subject: TestSubject 118 | 119 | @SetUp fun setup() { 120 | subject = TestSubject() 121 | } 122 | 123 | @Test fun test() { 124 | subject.method() // dereference directly 125 | } 126 | } 127 | **/ 128 | 129 | // 属性覆写参见成员覆写章节 130 | 131 | // 委托属性见委托属性章节 132 | 133 | 134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/builders/TypeSafeBuilders.kt: -------------------------------------------------------------------------------- 1 | package builders 2 | 3 | /** 4 | * User: mcxiaoke 5 | * Date: 16/1/21 6 | * Time: 15:52 7 | */ 8 | 9 | // https://kotlinlang.org/docs/reference/type-safe-builders.html 10 | // 类型安全的Builders 11 | // Type-Safe Builders 12 | 13 | interface Element { 14 | fun render(builder: StringBuilder, indent: String) 15 | } 16 | 17 | class TextElement(val text: String) : Element { 18 | override fun render(builder: StringBuilder, indent: String) { 19 | builder.append("$indent$text\n") 20 | } 21 | } 22 | 23 | abstract class Tag(val name: String) : Element { 24 | val children = arrayListOf() 25 | val attributes = hashMapOf() 26 | 27 | protected fun initTag(tag: T, init: T.() -> Unit): T { 28 | tag.init() 29 | children.add(tag) 30 | return tag 31 | } 32 | 33 | override fun render(builder: StringBuilder, indent: String) { 34 | builder.append("$indent<$name${renderAttributes()}>\n") 35 | for (c in children) { 36 | c.render(builder, indent + " ") 37 | } 38 | builder.append("$indent\n") 39 | } 40 | 41 | private fun renderAttributes(): String? { 42 | val builder = StringBuilder() 43 | for (a in attributes.keys) { 44 | builder.append(" $a=\"${attributes[a]}\"") 45 | } 46 | return builder.toString() 47 | } 48 | 49 | 50 | override fun toString(): String { 51 | val builder = StringBuilder() 52 | render(builder, "") 53 | return builder.toString() 54 | } 55 | } 56 | 57 | abstract class TagWithText(name: String) : Tag(name) { 58 | operator fun String.unaryPlus() { 59 | children.add(TextElement(this)) 60 | } 61 | } 62 | 63 | class HTML() : TagWithText("html") { 64 | fun head(init: Head.() -> Unit) = initTag(Head(), init) 65 | 66 | fun body(init: Body.() -> Unit) = initTag(Body(), init) 67 | } 68 | 69 | class Head() : TagWithText("head") { 70 | fun title(init: Title.() -> Unit) = initTag(Title(), init) 71 | } 72 | 73 | class Title() : TagWithText("title") 74 | 75 | abstract class BodyTag(name: String) : TagWithText(name) { 76 | fun b(init: B.() -> Unit) = initTag(B(), init) 77 | fun p(init: P.() -> Unit) = initTag(P(), init) 78 | fun h1(init: H1.() -> Unit) = initTag(H1(), init) 79 | fun a(href: String, init: A.() -> Unit) { 80 | val a = initTag(A(), init) 81 | a.href = href 82 | } 83 | } 84 | 85 | class Body() : BodyTag("body") 86 | class B() : BodyTag("b") 87 | class P() : BodyTag("p") 88 | class H1() : BodyTag("h1") 89 | 90 | class A() : BodyTag("a") { 91 | public var href: String 92 | get() = attributes["href"]!! 93 | set(value) { 94 | attributes["href"] = value 95 | } 96 | } 97 | 98 | fun html(init: HTML.() -> Unit): HTML { 99 | val html = HTML() 100 | html.init() 101 | return html 102 | } 103 | 104 | // 使用示例 105 | fun result(args: Array) = 106 | html { 107 | head { 108 | title { +"XML encoding with Kotlin" } 109 | } 110 | body { 111 | h1 { +"XML encoding with Kotlin" } 112 | p { +"this format can be used as an alternative markup to XML" } 113 | 114 | // an element with attributes and text content 115 | a(href = "http://kotlinlang.org") { +"Kotlin" } 116 | 117 | // mixed content 118 | p { 119 | +"This is some" 120 | b { +"mixed" } 121 | +"text. For more see the" 122 | a(href = "http://kotlinlang.org") { +"Kotlin" } 123 | +"project" 124 | } 125 | p { +"some text" } 126 | 127 | // content generated by 128 | p { 129 | for (arg in args) 130 | +arg 131 | } 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/com/mcxiaoke/kotlin/stdlib/Core.kt: -------------------------------------------------------------------------------- 1 | package com.mcxiaoke.kotlin.stdlib 2 | 3 | /** 4 | * User: mcxiaoke 5 | * Date: 16/1/21 6 | * Time: 16:28 7 | */ 8 | 9 | // Package kotlin 10 | // https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/index.html 11 | // 核心包,包含主要数据类型的定义 12 | 13 | // 数组构造函数 14 | // 每种原始类型都有对应的数组类型 15 | 16 | fun createArray() { 17 | var a1 = Array(10, fun(i): Int { 18 | return i * i 19 | }) 20 | println(a1.joinToString()) 21 | 22 | } 23 | 24 | fun createBooleanArray() { 25 | var a1 = BooleanArray(10, fun(i): Boolean { 26 | return i % 2 == 0 27 | }) 28 | println(a1.joinToString()) 29 | } 30 | 31 | // 数组的工厂方法,也是每个原始类型都有一个 32 | fun createArray2() { 33 | val a = arrayOf(1, 2, 3, 4, 5) 34 | val b = booleanArrayOf(true, false, true, true) 35 | } 36 | 37 | fun main(args: Array) { 38 | createArray() 39 | createBooleanArray() 40 | createArray2() 41 | } 42 | 43 | --------------------------------------------------------------------------------