《Kotin 编程思想·实战》
《Kotlin极简教程》正式上架:
点击这里 > 去京东商城购买阅读
点击这里 > 去天猫商城购买阅读
1 JVM语言家族概览
1.1 编程语言简史
1.2 程序执行的三种方式
1.2.1 编译执行
1.2.2 解释执行
1.2.3 虚拟机执行
1.3 JVM概述
1.3.1 Java源代码编译执行过程
1.3.2 Java Class文件简介
1.3.3 JVM字节码执行过程
1.4 JVM语言家族
1.4.1 Scala
1.4.2 Groovy
1.4.3 Clojure
1.4.4 Kotlin
1.4.5 Xtend
Xtend是Eclipse推出的一个新的JVM语言,并无意替代Java,而是以己之长补Java之短,精简代码,无类型,改进可读和维护。Eclipse Xtend可以编译成可读的Java代码,类似CoffeeScript之于Javascript。
静态类型
特点
扩展方法 :加强封闭类型的新功能。
Lambda表达式:匿名函数文字简洁的语法。
将lambda表达式编译成匿名内部类。
运算符重载:让库更表达。
强大的交换机的表达:类型与隐式类型转换开关。
多个调度:即多态方法调用。
模板表达式:智能空间处理。
报表:一切都是表达式。
属性:访问和定义getter和setter方法的速记法。
局部类型推理:很少需要写下类型签名了。
全面支持Java的泛型:包括所有的一致性和转换规则。
类型系统:Xtend的静态类型是不错的,因为它允许更好的静态分析和基于类型信息的更好的工具。然而,缺点是额外的复杂性
2 Kotlin简介
2.1 kotlin简史
2.2 快速学习工具
2.2.1 云端IDE
2.2.2 本地命令行环境搭建
2.2.3 Kotlin REPL
2.2.4 使用IntelliJ IDEA
2.2.5 使用Gradle构建工程
3 快速开始:HelloWorld
3.1 命令行的HelloWorld
3.2 应用程序版HelloWorld
3.3 Web RESTFul HelloWorld
3.4 Android版的HelloWorld
3.5 JavaScript(Canvas) 版HelloWorld
4 kotlinc编译过程分析
4.1 kotlinc执行原理分析
4.2 kotlin代码执行过程
4.3 Kt.class与Java.class区别
5 语言基础
5.1 基础语法
5.1.1 包(package)
package打包与import导包
源文件可以不需要匹配目录和包,源文件可以放在任何文件目录
如果没有任何包声明的话,则当中的代码都属于默认包,导入时包名即为函数名!
比如:import shortToast
另外你还可以在导入类的时候为类设置一个别名,比如:
import java.util.Date as d
直接在文件中写一堆fun方法!
kotlin中因为可以使用扩展方法,所以可以连class和interface都不写,
5.1.2 变量
变量作用域
声明变量
val
var
val定义常量和var定义变量,默认都是private的,比如
定义:val a =123, var b = 321,打开生成的.class文件可以看到:
private私有,且默认写了公有的getter和setter方法。
5.1.3 表达式
range
return
throw
三元表达式
Lambda表达式
this表达式
super表达式
5.1.4 代码块
5.1.5 分支控制流
if表达式
判断结构(条件表达式)
Java int max = a>b?a:b
Kotlin: val max = if (a>b) a else b
在if语句块的最后可以自动返回最后一行表达式的值,而不需要写return
fun ifExample(x: Int, y: Int) {
val result = if (x >= y) {
println("Condition ok.")
true
} else {
println("Condition else.")
false
}
println("Result $result")
}
when表达式
fun whenExample(userType: Int) {
when (userType) {
0 -> println("Registered user")
1 -> print("Administrator")
else -> {
println("Unknown")
}
}
}
fun whenExample2(userType: Int) {
when (userType) {
0, 1 -> println("Welcome user.")
else -> println("Permission denied.")
}
}
fun whenExample3(userType: Int) {
when (userType) {
filterUserType(userType) -> {
println("Subtype ok")
whenExample2(userType)
}
else -> print("Subtype not ok")
}
}
fun filterUserType(userType: Int): Int {
if (userType >= 0 && userType < 2) {
return userType;
}
return -1
}
fun whenExample4(x: Int) {
val from = 0
val to = 100
when (x) {
in from..to -> println("PRECISE")
in (from / 2)..(to / 2) -> print("VERY PRECISE")
50 -> print("STRAIGHT IN TARGET")
else -> print("MISSED")
}
}
fun whenExample5(fullName: String) {
val isJohn = when (fullName) {
is String -> fullName.startsWith("John ")
else -> false
}
}
fun whenExample6(fullName: String) {
when {
fullName.length == 0 -> println("Please enter your name.")
fullName.substring(0, 2).equals("X ") -> println("Hello Mr. X")
fullName.startsWith("John ") && !fullName.endsWith(" Smith") -> println("Hello John!")
fullName.endsWith(" Smith") -> println("Hello agent Smith.")
else -> println("Only secret agents allowed!")
}
}
5.1.6 循环
while循环
for循环
Kotlin中的while与do-while,break,continue与Java中的类似,不过Kotlin中多了个好玩的东西:
Ranages,包含与范围有关的函数操作符
Ranages
在范围内与不在范围内
fun main(array: Array<String>) {
for ((index, value) in array.withIndex()) {
println("[ $index ][ $value ]")
}
val a1 = 1
val a2 = 2
val b1 = if(a1 in 0..9) true else false
val b2 = if(a2 !in 10..20 ) true else false
println(b1)
println(b2)
val str1 = "Hello"
val str2 = "Hello,Wolrd"
if(str1 in str2) println(str1 + " in " + str2) else println(str1 + " is not in " + str2)
if(str1 in "Hello".."Kotlin") println(str1 + " in " + "Hello".."Kotlin") else println(str1 + " is not in " + "Hello".."Kotlin")
}
顺序遍历
val arr = Array(10,{n->n})
arr.forEach(::print)
println()
arr.forEach{
it->print(it.toString() + " ")
}
println()
for(e in arr) print(e)
println()
for(i in 0..arr.lastIndex) print(arr[i].toString() + " ")
println()
你也可以调lastIndex来获得最后的下标,写成if(i in 0..array.lastIndex)
如果你不想顺着遍历,想反过来遍历,可以利用downTo (递减)关键字,从最大值到最小值递减!
for(i in 9 downTo 5) print(arr[i])
println()
可能你还想隔着遍历,比如只遍历:10,7,4,1,可以用 step (步长)关键字!后面跟着的是步长,
比如你可以写成小数0.1这样也行,示例:
for(i in 9 downTo 3 step 3) print(arr[i])
倒序遍历
5.1.7 代码注释
5.1.8 异常
Kotlin中所有的Exception都继承了Throwable,含有一个message且未经检查。
这表示不会强迫我们在任何地方使用try/catch,而Java中如果某个方法抛出
了Exception,就需要用try-catch包围代码块。
Kotlin抛出异常和try-catch-finally和Java中的类似!但是Kotlin中throw和try都是表达式,
意味着他们可以赋值给某个变量,这一点在处理边界问题的时候很有用!代码示例:
class ExceptionExamples {
fun exceptionExmple() {
try {
// do something ...
} catch (e: KotlinNullPointerException) {
// handle exception
} finally {
// do something ...
}
}
// Try / Catch is expression!
fun exceptionExample2(): Int {
return try {
// do something
0
} catch (e: KotlinNullPointerException) {
// handle exception
-1
}
}
}
5.2 标识符
5.2.1 修饰符
访问权限
public:默认,总是可见
internal:同模块可见
private:声明范围与同模块的子作用域可见
protected:类似于private,但对子类也可见
5.2.2 关键保留字
var:定义变量
val:定义常量
fun:定义方法
Unit:默认方法返回值,类似于Java中的void,可以理解成返回没什么用的值
vararg:可变参数
如果是可变参数的话,可以使用 vararg 关键字修饰
fun sum(vararg args: Int) {
var sum = 0
for (x in args) {
sum += x
}
println("Sum: $sum")
}
fun trySum(){
sum(1, 3, 6, 10, 1, 2, 3, 4)
}
$:字符串模板(取值)
位运算符:or(按位或),and(按位与),shl(有符号左移),shr(有符号右移),
ushr(无符号右移),xor(按位异或),inv(按位取反)
in:在某个范围中
downTo:递减,循环时可用,每次减1
step:步长,循环时可用,设置每次循环的增加或减少的量
when:Kotlin中增强版的switch,可以匹配值,范围,类型与参数
is:判断类型用,类似于Java中的instanceof()
5.2.3 运算符
5.2.4 赋值符
5.3 函数
5.3.1 main函数
5.3.2 定义函数
使用 fun 关键字来声明
如果没有访问控制符修饰的fun默认是public final的!
返回值:Unit
扩展函数
直接定义在文件中,而不需要依赖于任何的类的函数
成员函数
写在class或object中的函数
5.3.3 包级函数
5.3.4 Lambda表达式
// lambda写法1
val runnable3 = Runnable { ->
println("I'm a Lambda")
}
// lambda写法2
val runnable4 = { println("I'm a Lambda") }
Thread(runnable4).start()
函数式接口(functional interface)
只包含一个抽象方法的接口
Java标准库中的java.lang.Runnable和java.util.Comparator
public void runThread() {
new Thread(new Runnable() {
public void run() {
System.out.println("Run!");
}
}).start();
}
public void runThreadUseLambda() {
new Thread(() -> {
System.out.println("Run!");
}).start();
}
Collections.sort(list, (x, y) -> y - x);
List input = Arrays.asList(new String[] {"apple", "orange", "pear"});
input.forEach((v) -> System.out.println(v));
input.forEach(System.out::println);
5.3.5 闭包
5.3.6 匿名函数
// new 一个线程
// 匿名类写法
val runnable1 = object : Runnable{
override fun run() {
println("I'm an anonymous class")
}
}
// 函数写法, 略像js
val runnable2 = fun (){
println("I'm a function")
}
5.4 特色功能
5.4.1 函数拓展和属性拓展(Extensions)
fun main(args: Array<String>) {
val list = listOf("1", "2", "3", "4")
// 函数拓展
list.myForEach { println(it) }
// 属性拓展
println("last: ${list.lastItem}")
}
/**
- 拓展 List 类, 加一个自定义的遍历方法
*/
fun <T> List<T>.myForEach(doTask: (T) -> Unit){
for(item in this)
doTask(item)
}
/**
- 拓展 List 类, 加一个自定义的长度属性
*/
val <T> List<T>.lastItem: T
get() = get(size - 1)
// 输出:
1
2
3
4
last: 4
class Employee {
fun name(): String {
return "Employee name"
}
}
class ExtendedFunctionalityExample(val e: Employee) {
// We extended Employee class with function that does not exist in original class!
fun Employee.age(): Int {
return 25
}
fun tryExtendedEmployeeExample() {
println("Name: ${e.name()}, Age: ${e.age()}")
}
}
5.4.2 属性代理
以懒加载为例,lazySum可能需要复杂的运算,我们把它代理给lazy。 可以看到,只有第一次加载进行了计算,之后都是直接取值,提高了效率。
val lazySum: Int by lazy {
println("begin compute lazySum ...")
var sum = 0
for (i in 0..100)
sum += i
println("lazySum computed!\n")
sum // 返回计算结果
}
fun main(args: Array<String>) {
println(lazySum)
println(lazySum)
}
// 输出:
begin compute lazySum ...
lazySum computed!
5050
5050
5.4.3 委托(Delegate)
5.4.4 空指针安全
空对象检查Null Check
var mNullable: String? = null
var mNonNull: String = "mNonNull"
fun testNull(){
println("testNull: ")
println(mNullable?.length)
println(mNonNull.length)
println()
}
// 输出:
testNull:
null
8
// java 风格,判空
if(mNullable != null)
mNullable.length
// kotlin 语法糖,判空(推荐)
mNullable?.length
null check实现原理简析
空类型强转为非空类型
var user: User? = getUser()
user!!.name = "Jack"
5.4.5 Lazy Evaluation
class UsesLazy {
val myLazyValue: String by lazy {
println("I am initializing this lazy value!")
"My lazy value!"
}
}
fun useLazy(){
val usesLazy: UsesLazy = UsesLazy()
val a: String = usesLazy.myLazyValue
val b: String = usesLazy.myLazyValue
val c: String = usesLazy.myLazyValue
}
6 类型系统
6.1 编译时类型与运行时类型
6.2 根类型Any
对象相等性
6.3 基本类型(Primitive Types)
6.3.1 Number: 包含整型与浮点型等
kotlin.Byte
kotlin.Short
kotlin.Int
kotlin.Long
kotlin.Float
kotlin.Double
6.3.2 Char: 字符类型(Character)
6.3.3 Boolean: 布尔类型
6.3.4 String: 字符串类型
字符串常量
字符串函数
字符串模板
转义字符串
Kotlin居然没有自动转型
for(i in 0..arr.lastIndex) print(arr[i] + " ") 不能自动转型,这样写代码多麻烦
for(i in 0..arr.lastIndex) print(arr[i].toString() + " ")
6.3.5 Array: 数组类型
原生数组类型
创建数组
定长数组:val fixedSizeArray = arrayOfNulls<Int>(10)
空数组: val empty = emptyArray<Int>()
装箱操作:val arr = arrayOf(1, 2, 3) //还有其他比如IntArrayOf,BooleanArrayOf等
闭包初始化:
val arr = Array(100, {num -> num})
for(i in 0..99) println(arr[i])
访问数组
使用[]
[]访问数组元素在这里实际上是进行了操作符的
重载,调用的其实是Array类的getter和setter方法,但是编译成字节码的时候会进行优化,
变成直接访问数组的内存地址,所以并不会造成性能损失!
遍历数组
foreach遍历
for(e in arr) println(e)
根据下标遍历
for(i in arr.indices) println(arr[i])
indices代表下标!范围:(0 <= indices < 数组size)
6.4 特殊类型
kotlin.Any
kotlin.Nothing
kotlin.Unit
kotlin.KClass<T>
6.5 可空类型(Nullable Types)
6.6 函数类型( Functional Types)
闭包类型
6.7 类型检测
is运算符
as运算符
6.8 类型转换
6.9 类型别名typealias
6.10 泛型
fun <T> genericFunctionsExample(x: T){
println("Value: $x")
}
fun tryGenericFunctionsExampe(){
genericFunctionsExample(5)
genericFunctionsExample("Some word!")
genericFunctionsExample('c')
genericFunctionsExample(5.55)
}
7 面向对象编程(OOP)
7.1 面向对象思想
7.2 类与继承
7.2.1 类
类的横向分类
抽象类
接口类
枚举类
注解类
静态类与伴生对象
sealed 密封类
sealed class SuperEnum {
class Human(val race: String) : SuperEnum()
class Animal(val specie: String, val legsCount: Int) : SuperEnum()
object Bacteria : SuperEnum()
}
fun trySuperEnum(superEnum: SuperEnum): String = when (superEnum) {
is SuperEnum.Human -> "Human ${superEnum.race}"
is SuperEnum.Animal -> "${superEnum.specie} with ${superEnum.legsCount} legs."
is SuperEnum.Bacteria -> "Some micro organism ..."
}
data 数据类
data class Person(val name: String, val age: Int){}
fun tryDataClassCopying(){
val p = Person("John Smith", 1985)
val p2 = p.copy()
val p3 = p.copy(age = 1990)
val p4 = p.copy("John Doe")
println(p)
println(p2)
println(p3)
println(p4)
}
data class Employee(val name: String, val age: Int) {}
data class Worker(val name: String = "Unknown", val age: Int = 1970) {}
类的纵向组合
嵌套类Nested Class
内部类Inner Class
匿名内部类Inner Class
声明类
类修饰符
构造函数
主构造函数
次构造函数
类的属性(数据结构)
类的行为(算法函数)
7.2.2 接口与抽象类
接口的默认实现
interface A {
fun foo() { println("A") } // 默认实现, 打印"A"
fun bar()
}
interface B {
fun foo() { println("B") }
fun bar() { println("bar") }
}
// 多继承时,显式指定 super<A>.foo() 以去冲突
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super.bar()
}
}
考虑下面的一个简单的进行货币转换的接口。该接口的实现方式可能是调用第三方提供的服务来完成实际的转换操作。
public interface CurrencyConverter {
BigDecimal convert(Currency from, Currency to, BigDecimal amount);
}
该接口在开发出来之后,在应用中得到了使用。在后续的版本更新中,第三方服务提供了新的批量处理的功能,允许在一次请求中同时转换多个数值。最直接的做法是在原有的接口中添加一个新的方法来支持批量处理,不过这样会造成已有的代码无法运行。而默认方法则可以很好的解决这个问题。使用默认方法的新接口如下所示。
public interface CurrencyConverter {
BigDecimal convert(Currency from, Currency to, BigDecimal amount);
default List convert(Currency from, Currency to, List amounts) {
List result = new ArrayList();
for (BigDecimal amount : amounts) {
result.add(convert(from, to, amount));
}
return result;
}
}
新添加的方法使用default关键词来修饰,并可以有自己的方法体。
目的
接口的默认方法的主要目标之一是解决接口的演化问题。当往一个接口中添加新的方法时,可以提供该方法的默认实现。对于已有的接口使用者来说,代码可以继续运行。新的代码则可以使用该方法,也可以覆写默认的实现。
实现行为的多继承
7.2.3 继承
open类
7.2.4 实现接口
7.2.5 函数重载
override重写覆盖父类函数
7.3 类的实例对象
新建对象
对象属性(数据结构)
对象行为(算法函数)
7.4 委托
类的委托
属性的委托
8 函数式编程(FP)
8.1 函数式编程概述
面向对象编程OOP特征
函数式编程FP特征
8.2 Kotlin函数式编程
8.2.1 函数是什么
内联函数
8.2.2 函数指针
8.2.3 复合函数(高阶函数)
8.2.4 闭包(closure)
js闭包
function closureExample(objID, text, timedelay) {
setTimeout(function() {
document.getElementById(objID).innerHTML = text;
}, timedelay);
}
closureExample(‘myDiv’, ‘Closure is created’, 500);
groovy闭包
Kotlin闭包
val test = if (5 > 3) {
print("yes")
} else {
print("no")
}
fun tryClosures() {
val values = listOf<Int>(2, 4, 6, 8, 10)
var result = 0
values.forEach {
result += it
}
println("Result: $result")
}
函数、Lambda、if语句、for、when,都可以称之为闭包
自执行闭包
自执行闭包就是在定义闭包的同时直接执行闭包,一般用于初始化上下文环境。 例如:
{ x: Int, y: Int ->
println("${x + y}")
}(1, 3)
节制的自由
使用闭包写代码相当灵活自由,省略了很多的临时变量和参数声明。 然而,也正是因为闭包的灵活性,造成如果泛滥的话,可能会写出可读性非常差的代码。
8.2.5 Lambda表达式(匿名函数)
Lambda 表达式俗称匿名函数,熟悉Java的大家应该也明白这是个什么概念。Kotlin 的 Lambda表达式更“纯粹”一点, 因为它是真正把Lambda抽象为了一种类型,而 Java 8 的 Lambda 只是单方法匿名接口实现的语法糖罢了。
val printMsg = { msg: String ->
println(msg)
}
fun main(args: Array<String>) {
printMsg.invoke("hello")
}
以上是 Lambda 表达式最简单的实例。
首先声明了一个名为 printMsg 的 Lambda,它接受一个 String 类型的值作为参数,然后在 main 函数中调用它。如果还想省略,你还可以在调用时直接省略invoke,像函数一样使用。
fun main(args: Array<String>) {
printMsg("hello")
}
Lambda 表达式作为高阶函数的参数传递
fun main(args: Array<String>) {
log("world", printMsg)
}
val printMsg = { str: String ->
println(str)
}
val log = { str: String, printLog: (String) -> Unit ->
printLog(str)
}
这个例子中,log 是一个接受一个 String 和一个以 String 为参数并返回 Unit 的 Lambda 表达式为参数的 Lambda 表达式。
8.2.6 内联函数(inline)
使用 高阶函数 在运行时会带来一些不利: 每个函数都是一个对象, 而且它还要捕获一个闭包, 也就是, 在函 数体内部访问的那些外层变量. 内存占用(函数对象和类都会占用内存) 以及虚方法调用都会带来运行时的消耗.
但是也不是说所有的函数都要内联,因为一旦添加了 inline 修饰,在编译阶段,编译器将会把函数拆分,插入到调用出。如果一个 inline 函数是很大的,那他会大幅增加调用它的那个函数的体积。
infix fun Double.powerPI(x: Int): Double {
return Math.pow(this, x + Math.PI)
}
fun infixExample() {
val array = arrayOf(2.0, 4.0, 6.0, 8.0, 10.0)
for (value in array) {
val result = value powerPI 5
println("Result: [ $value ][ $result ]")
}
}
8.2.7 本地函数(Local Functions)
fun worker() {
fun doWork(work: String) {
println("Working [ $work ]")
}
doWork("Importing user data")
doWork("Processing user data")
doWork("Exporting user data")
}
8.2.8 命名参数(NamedParameters)
fun addEmployee(name: String, year: Int, address: String) {
}
// Let's call method:
fun namedParametersExaple() {
addEmployee(
name = "John Smith",
year = 1985,
address = "Avenue 666"
)
}
8.2.9 外部函数external
8.2.10 尾递归tailrec
tailrec fun tailRecursiveExample(word: String) {
if (word.length == 1) {
println("--- end")
} else {
println(word)
tailRecursiveExample(word.substring(0..(word.length - 2)))
}
}
fun tryTailRecursiveExample() {
tailRecursiveExample("My word")
}
8.3 函数式Stream API
8.3.1 filter函数
8.3.2 map函数
fun main(args: Array<String>){
val list = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
list.filter { it%2==0 } // 取偶数
.map{ it*it } // 平方
.sortedDescending() // 降序排序
.take(3) // 取前 3 个
.forEach { println(it) } // 遍历, 打印
}
8.3.3 forEach
9 Kotlin与Java互操作(Interoperability)
9.1 使用工具互相转换
9.1.1 将 Java 转换为 Kotlin
9.1.2 将 Kotlin 转换为 Java
9.1.3 用 Kotlin 兼容 Java 的做法,本来就是权宜之计,兼容必然带来新旧两种观念的冲突以及丑陋的发生
9.2 Kotlin与Java互操作
9.2.1 Kotlin无缝调用第三方jar库
9.2.2 执行shell
9.2.3 文件操作
9.2.4 多线程代码
9.3 Kotlin与Java的区别
9.3.1 void 与 Unit
9.3.2 反射获取类的 Class
9.3.3 Java 与 Kotlin 关键字冲突的处理
9.3.4 static 方法与伴生对象companion object
9.3.5 包级别函数
9.3.6 重载必须使用override
10 集合类与泛型
10.1 kotlin.collections
10.1.1 不可变集合类
List
// Gives us read only list
fun getList(): List<Int> {
return listOf(1, 3, 5, 7)
}
// Gives us read only map
fun getMap(): Map<String, Double> {
return mapOf("EUR" to 123.20, "USD" to 110.34, "CHF" to 111.4)
}
fun printMap(key: String) {
val map = getMap()
if (key in map) println(map[key]) else println("There is no such key $key")
}
Set
Map
fun traverseMapExample(map: Map<String, Int>) {
for ((key, value) in map) println("$key : $value")
}
10.1.2 可变集合类
kotlin.collections.MutableCollection<E>
MutableList<E>
MutableSet<E>
MutableMap<K, V>
10.2 泛型与类型安全
10.2.1 类型参数
10.2.2 类型推测
10.2.3 协变与逆变
10.3 类型上下界
11 轻量级线程:协程(Coroutines)
11.1 协程概念
val fibonacci = buildSequence {
yield(1) // first Fibonacci number
var cur = 1
var next = 1
while (true) {
yield(next) // next Fibonacci number
val tmp = cur + next
cur = next
next = tmp
}
}
...
for (i in fibonacci){
println(i)
if(i > 100) break //大于100就停止循环
}
用看似同步的代码做着异步的事情
11.2 协程的基本操作
11.2.1 创建
11.2.2 启动
11.2.3 暂停
11.2.4 继续
11.3 竞争条件
11.4 同步
11.5 实现异步
12 使用Kotlin开发Web应用
12.1 Kotlin集成Spring Boot开发Web应用
12.1.1 Spring Boot简介
12.1.2 Kotlin集成Spring Boot
12.2 Spring 5 对 Kotlin的支持
12.2.1 Functional bean registration with Kotlin
Spring Framework 5.0 introduces a new way to register beans using lambda as an alternative to XML or JavaConfig with @Configuration and @Bean. In a nutshell, it makes it possible to register beans with a Supplier lambda that acts as a FactoryBean.
In Java you will for example write:
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(Foo.class);
context.registerBean(Bar.class, () -> new
Bar(context.getBean(Foo.class))
);
While in Kotlin, reified type parameters allow us to simply write:
val context = GenericApplicationContext {
registerBean<Foo>()
registerBean { Bar(it.getBean<Foo>()) }
}
12.2.2 使用Kotlin的函数式风格API开发 Web应用
Spring Framework 5.0 comes with a Kotlin routing DSL that allows you to leverage the Spring Functional Web API recently announced with clean and idiomatic Kotlin code:
{
("/blog" and accept(TEXT_HTML)).route {
GET("/", this@BlogController::findAllView)
GET("/{slug}", this@BlogController::findOneView)
}
("/api/blog" and accept(APPLICATION_JSON)).route {
GET("/", this@BlogController::findAll)
GET("/{id}", this@BlogController::findOne)
}
}
https://github.com/EasyKotlin/mixit
12.2.3 Kotlin Script based templates
ScriptTemplateView to render templates
This could allow you to write this kind of templates with full autocompletion and refactoring support in your IDE:
import io.spring.demo.*
"""
${include("header")}
<h1>${i18n("title")}</h1>
<ul>
${users.joinToLine{ "<li>${i18n("user")} ${it.firstname} ${it.lastname}</li>" }}
</ul>
${include("footer")}
"""
https://github.com/EasyKotlin/kotlin-script-templating
12.3 使用Kotlin的Web框架Ktor开发Web应用
Ktor is a framework for quickly creating web applications in Kotlin with minimal effort.
import org.jetbrains.ktor.netty.*
import org.jetbrains.ktor.routing.*
import org.jetbrains.ktor.application.*
import org.jetbrains.ktor.host.*
import org.jetbrains.ktor.http.*
import org.jetbrains.ktor.response.*
fun main(args: Array<String>) {
embeddedServer(Netty, 8080) {
routing {
get("/") {
call.respondText("Hello, world!", ContentType.Text.Html)
}
}
}.start(wait = true)
}
https://github.com/Kotlin/ktor/wiki
12.4 基于Kotlin Script的模板引擎
13 使用Kotlin实现DSL
13.1 DSL
13.2 Groovy的DSL语法
13.3 Kotlin使用闭包构建 DSL
14使用Kotlin开 发JavaScript代码
14.1 Kotlin代码编译成js过程
14.2 使用Kotlin开发JavaScript代码
15 使用Kotlin开发Android程序
https://github.com/Kotlin/anko
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
MyActivityUI().setContentView(this)
}
}
class MyActivityUI : AnkoComponent<MyActivity> {
override fun createView(ui: AnkoContext<MyActivity>) = ui.apply {
verticalLayout {
editText()
button("Say Hello") {
onClick { ctx.toast("Hello!") }
}
}
}.view
}
16 使用Kotlin Native开发原生应用
17 KOTLIN语言生态
17.1 测试(Testing)
17.2 依赖注入(Dependency Injection)
17.3 JSON序列化(JSON serialization)
17.4 Web 框架
17.5 数据库访问(Database access)
17.6 工具类(Utilities)
17.7 桌面编程(Desktop programming)
17.8 Http库
Fuel: https://github.com/EasyKotlin/Fuel
//an extension over string (support GET, PUT, POST, DELETE with httpGet(), httpPut(), httpPost(), httpDelete())
"http://httpbin.org/get".httpGet().responseString { request, response, result ->
//do something with response
when (result) {
is Result.Failure -> {
error = result.getAs()
}
is Result.Success -> {
data = result.getAs()
}
}
}
//if we set baseURL beforehand, simply use relativePath
FuelManager.instance.basePath = "http://httpbin.org"
"/get".httpGet().responseString { request, response, result ->
//make a GET to http://httpbin.org/get and do something with response
val (data, error) = result
if (error != null) {
//do something when success
} else {
//error handling
}
}
//if you prefer this a little longer way, you can always do
//get
Fuel.get("http://httpbin.org/get").responseString { request, response, result ->
//do something with response
result.fold({ d ->
//do something with data
}, { err ->
//do something with error
})
}
val (request, response, result) = "http://httpbin.org/get".httpGet().responseString() // result is Result<String, FuelError>
17.9 并发库kotlinx.coroutines
kotlinx.coroutines:https://github.com/EasyKotlin/kotlinx.coroutines
https://github.com/EasyKotlin/kotlinx.coroutines/blob/master/coroutines-guide.md
fun main(args: Array<String>) {
launch(CommonPool) { // create new coroutine in common thread pool
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello,") // main function continues while coroutine is delayed
Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}
Run this code:
Hello,
World!
18 附录 参考资料
https://github.com/EasyKotlin/kotlin-in-action
Awesome Kotlin: https://kotlin.link/
Kotlin项目Github源码:https://github.com/JetBrains/kotlin
Kotlin语言规范:http://jetbrains.github.io/kotlin-spec/
在线体验学习Kotlin语言:https://try.kotlinlang.org
官网文档:http://kotlinlang.org/docs/
https://github.com/trending?l=kotlin
https://github.com/EasyKotlin/Kotlin-for-Android-Developers
https://github.com/EasyKotlin/Bandhook-Kotlin
https://spring.io/blog/2017/01/04/introducing-kotlin-support-in-spring-framework-5-0
xtend: http://www.eclipse.org/xtend/documentation/index.html