2019年7月

定义 Packages

应该在文件的最顶部定义包名

package my.demo

import java.util.*

//...
文件名与包名并不一定必须一致。

定义函数

定义一个接口两个数字,返回它们和的函数

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

只有一个表达式作为主体的函数,Kotlin 会自动推断出它的返回类型

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

函数默认会返回无具体意义的值

fun printSum(a: Int, b: Int): Unit {
  println("Sum of $a and $b is ${a + b}")
}

Unit 类型是可以被省略的

fun printSum(a: Int, b: Int) {
  println("Sum of $a and $b is ${a + b}")
}

定义变量

使用 val 定义一个本地只读的只能被赋值一次的变量

val a: Int = 1 // 立即赋值
val b = 2 // `Int` 类型是被推断出来的
val c: Int // 先定义一个 `Int` 类型的变量
c = 3 // 然后再赋值

也可以使用 var 定义一个可以被多次赋值的变量

var x = 5
x += 1

上层变量

val PI = 3.14
var x = 0

fun incrementX() {
  x += 1
}

注释

就如同 Java、JavaScript,Kotlin 同时支持文件行末注释以及块注释:

// 这是一个行末注释

/* 这是一个
  块级注释 */
与 Java 不同点在于,Kotlin 的块注释支持嵌套

使用字符模板

var a = 1
// 模板中和简单名称
val s1 = "a 的值为 $a"

a += 1

// 在模板中的任意格式
val s2 = "${s1.replace("为", "以前为")},但是现在为 $a"

使用条件表达式

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

使用 if 表达示

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

使用可为 null 的值以及对 null 的检查

对于一个可为 null 的值,必须显示的标记它可能为 nullnullable

fun parseInt(str: String): Int? {
  // ...
}

在函数中返回可为 null 的值

fun printProduct(arg1: String, arg2: String) {
  val x = parseInt(arg1)
  var y = parseInt(arg2)

  // 使用 `x * y` 的话,会报错误,因为它们有可能为 `null`
  if (x != null & y != null) {
    // 在 `null` 检查完成之后,`x` 与 `y` 会自动的被转为不会为 `null`(`non-nullable`) 的值
    println(x * y)
  }
  else {
    println("'$arg1' 与 '$arg2' 不是都为数字")
  }
}

或者

if (x == null) {
  println("$arg1 的类型错误")
  return
}

if (y == null) {
  println("$arg2 的类型错误")
  return
}
println(x * y)

使用类型检查以及自动类型转换

is 关键字会检测一个值是否是某个类型的,只要完成类型检查,那么被检查的值就可以直接当作该类型去使用。

fun getStringLength(obj: Any): Int? {
  if (obj is String) {
    // `obj` 已经被自动转换为 `String` 类型了
    return obj.length
  }
  return null
}

或者

fun getStringLength(obj: Any): Int? {
  if (obj !is String) return null
 
  return obj.length
}

也可以

fun getStringLength(obj: Any): Int? {
  if (obj is String && obj.length > 0) {
    return obj.length
  }
  return null
}

使用 for 循环

val items = listOf("apple", "banana", "kiwifrut")
for (item in items) {
  println(item)
}

或者

val items = listOf("apple", "banana", "kiwifruit")
for (index in items.indices) {
  println("item at $index is ${items[index]}")
}

使用 while 循环

val items = listOf("apple", "banaba", "kiwifruit")
var index = 0
while (index < items.size) {
  println("item at $index is ${items[index]}")
  index += 1
}

使用 when 表达式

fun describe(obj: Any): String = 
  when (obj) {
    1 -> "One"
    "Hello" -> "Greeting"
    is Long -> "Long"
    !is String -> "Not a string"
    else -> "Unknown"
  }

使用 ranges

检查一个数字是否在一个范围内

val x = 10
val y = 9
if (x in 1..y+1) {
  println("fits in range")
}

检查一个值是否不在一个范围内

val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
  println("-1 不在范围内")
}
if (list.size in !in list.indices) {
  println("list.size 同样的也不在 list.indices 范围内")
}

遍历一个范围

for (x in 1..5) {
  print(x)
}

或者可以指定一个步进值

for (x in 1..10 step 2) {
    print(x)
}
println()
for (x in 9 downTo 0 step 3) {
    print(x)
}

使用集合

遍历一个集合

for (item in items) {
  println(item)
}

使用 in 确定一个集合是否包含某个对象

when {
  "orange" in items -> println("juicy")
  "apple" in items -> println("apple is fine too")
}

在集合上使用 filtermap

val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
  .filter { it.startsWith("a") }
  .sortedBy { it }
  .map { it.toUpperCase() }
  .forEach { println(it) }

创建基础类实例

val rectangle = Rectangle(5.0, 2.0) // 不需要 `new` 关键字
val triangle = Triangle(3.0, 4.0, 5.0)

开发计算机软件是一个复杂的过程,在过去很长的一段时间时间里,计算机软件的开发被开发者们规整为以下这些常见的不同活动(Activity):

  1. 定义问题(Problem Definition)
  2. 需求分析(Requirements Development)
  3. 规划构建(Construction Planning)
  4. 软件架构(Software Architecture)或者高层设计(High-level Design)
  5. 详细设计(Detailed Design)
  6. 编码调试(Coding and Debugging)
  7. 单元测试(Unit Testing)
  8. 集成测试(Integration Testing)
  9. 集成(Integration)
  10. 系统测试(System Testing)
  11. 保障维护(Corrective Maintenance)

构建有时候被简单的理解为编码(Coding)或者编程(Programming),但是编码并不算是最贴切的,因为它有一种“把已经存在的设计机械化地翻译成计算机语言”的意味,而构建并不都是这么机械化的,需要可观的创造力与判断力。下面列出了一些软件构建活动中的具体任务(Task):

  • 验证有关的基础工作都已经完成,因此构建活动可以顺利的进行下去
  • 确定如何测试所写的代码
  • 设计并编写类(Class)和子程序(Routine)
  • 创建并命名变量(Variable)和具名常量(Named Constant)
  • 选择控制结构(Control Structure),组织语句块
  • 对你的代码进行单元测试和集成测试,并排除其中的错误
  • 评审开发团队其他成员的底层设计和代码,并让他们评审你的工作
  • 润饰代码,仔细进行代码的格式化和注释
  • 将单独开发的多个软件组件集成为一体
  • 调整代码(Tuning Code),让它更快,更省资源

为什么构建活动很重要?

  • 构建活动是软件开的主要组成部分
  • 构建活动是软件开发中的核心活动
  • 把主要精力集中于构建活动,可以大大提高程序员的生产率
  • 构建活动的产中物 —— 源代码 —— 往旆是对软件的唯一精确描述
  • 构建活动是唯一一项确保会完成的工作

用隐喻更充分的理解软件开发

你走进一间安全严密、温度精确控制在 20 摄氏度的房间,并在里面发现了病毒(Virus)、特洛伊木马(Trojan horse)、蠕虫(Worm)、臭虫(Bug)、逻辑炸弹(Logic bomb)、崩溃(Crash)、论坛口水战(Flame)、双绞线转换头(Twisted sex changer),还有致使错误(Fatal Error)……

重要的研发成果常常产自类比(Analogy),通过把不太理解的东西和一些你较为理解、且十分类似的东西做比较,可以对不理解的东西产生更深刻的理解,这种使用隐喻的方法,叫作“建模(Modeling)”。建模有其不好的一面,但是总的来说,模型是有好处的,它的威力在于其生动性,让你能够把握整个概念,能隐隐地暗示各种属性(Properties)、关系(Relationships)以及需要补充查证的部分(Additional areas of inquiry)。

隐喻的价值绝不应实低估,隐喻的优点在于其可预期的效果,能被所有人理解。不必要的沟通和误解也因此大为减少,学习与教授更为快速。实际上,隐喻是对概念进行内在化(Internalizing)和抽象(Abstracting)的一种途径,它让人们在更高的层面上思考问题,从而避免低层次的错误

-- Fernando J. Corbato

人地心说到日心说,从以计算机为中心到以数据为中心

托勒密的地心说模型持续了1400年,直到1543年哥白尼提出了以太阳为中心的理论,最终帮助人们找到了更多的行星,并将月亮重新界定为地球的卫星而不是一个独立的行星,它使人们对人类在宇宙中的地位有了一个完全不同的理解。

20世纪70年代早期计算机编程方面的变化就像从日心说到地心说一样,以前的编程观点是以计算机为中心(Computer-centered),数据处理是把所有的数据看作是流经计算机(Flowing through a computer)的连续卡片流(stream of crads),而后转变为以数据为中心(Database-centered),把焦点放到的数据池(Pool of data),而计算机偶尔涉足其中。

与其说是隐喻是一张路线图,不如说它是探路明灯,它不会告诉你该去哪里寻找答案,而是告诉你该如何去寻找答案,隐喻的作用更像是启示(Heuristic),而不是算法(Algorithm)。算法是一套定义明确的指令,使你能完成某个特定的任务,算法是可预测的(Predictable)、确定性的(Deterministic)不易变化的(Not subject to chance)。

启发式方式是一种帮你寻求答案的技术,但是它给出的答案是具有偶然性的(Subject to chance)