kotlin学习
为什么要使用kotlin
- 减少模板代码
- 安全,避免空指针异常
- 互操作性: 充分利用 JVM、Android 和浏览器的现有库
- 工具友好: 可用任何 Java IDE 或者使用命令行构建
Kotlin安装
brew update
brew install kotlin
HelloWorld Kotlin
touch hello.kt //kotlin的文件用kt结尾
并加入内容:
fun main(args: Array<String>) {
println("Hello, World!")
}
- 编译并运行hello.kt
kotlinc hello.kt -include-runtime -d hello.jar //编译
//-d:用来设置编译输出的名称,可以是 class 或 .jar 文件,也可以是目录
//-include-runtime : 让 .jar 文件包含 Kotlin 运行库,从而可以直接运行。
java -jar hello.jar //运行
kotlin -classpath hello.jar HelloKt //也可以运行,HelloKt为编译器为hello.kt文件生成的默认类名
Kotlin交互式解释器REPL
> kotlinc-jvm
Welcome to Kotlin version 1.2.71 (JRE 1.8.0_172-b11)
Type :help for help, :quit for quit
>>> 2+2
4
>>>
Kotlin作为脚本语言使用
Kotlin 也可以作为一个脚本语言使用,文件后缀名为 .kts
例如我们创建一个名为 list_folders.kts,代码如下:
import java.io.File
val folders = File(args[0]).listFiles { file -> file.isDirectory() }
folders?.forEach { folder -> println(folder) }
执行时通过 -script 选项设置相应的脚本文件。
kotlinc -script list_folders.kts <path_to_folder>
Kotlin语法
默认导入包
有多个包会默认导入到每个 Kotlin 文件中:
- kotlin.*
- kotlin.annotation.*
- kotlin.collections.*
- kotlin.comparisons.*
- kotlin.io.*
- kotlin.ranges.*
- kotlin.sequences.*
- kotlin.text.*
函数定义
函数定义使用关键字 fun,参数格式为 参数 : 类型
fun sum(a: Int, b: Int): Int { // Int 参数,返回值 Int
return a + b
}
表达式作为函数体
fun sum(a: Int, b: Int) = a + b
public fun sum(a: Int, b: Int): Int = a + b // public 方法则必须明确写出返回类型
无返回值的函数(类似Java中的void)
fun printSum(a: Int, b: Int): Unit {
print(a + b)
}
// 如果是返回 Unit类型,则可以省略(对于public方法也是这样):
public fun printSum(a: Int, b: Int) {
print(a + b)
}
可变长参数函数
函数的变长参数可以用 vararg 关键字进行标识:
fun vars(vararg v:Int){
for(vt in v){
print(vt)
}
}
// 测试
fun main(args: Array<String>) {
vars(1,2,3,4,5) // 输出12345
}
lambda(匿名函数)
// 测试
fun main(args: Array<String>) {
val sumLambda: (Int, Int) -> Int = {x,y -> x+y}
println(sumLambda(1,2)) // 输出 3
}
定义常量与变量
var <标识符> : <类型> = <初始化值> //var 关键字,可变变量定义
val <标识符> : <类型> = <初始化值> //val 关键字,不可变变量定义,类似Java final变量
val a: Int = 1
val b = 1 // 系统自动推断变量类型为Int
val c: Int // 如果不在声明时初始化则必须提供变量类型
c = 1 // 明确赋值
var x = 5 // 系统自动推断变量类型为Int
x += 1 // 变量可修改
Kotlin注释
Kotlin 支持单行和多行注释,实例如下:
// 这是一个单行注释
/* 这是一个多行的
块注释。 */
字符串模板
$ 表示一个变量名或者变量值
$varName 表示变量值
${varName.fun()} 表示变量的方法返回值
var a = 1
// 模板中的简单名称:
val s1 = "a is $a"
a = 2
// 模板中的任意表达式:
val s2 = "${s1.replace("is", "was")}, but now is $a"
NULL检查机制
Kotlin的空安全设计对于声明可为空的参数,在使用时要进行空判断处理,有两种处理方式,字段后加!!像Java一样抛出空异常,另一种字段后加?可不做处理返回值为 null或配合?:做空判断处理
//类型后面加?表示可为空
var age: String? = "23"
//抛出空指针异常
val ages = age!!.toInt()
//不做处理返回 null
val ages1 = age?.toInt()
//age为空返回-1
val ages2 = age?.toInt() ?: -1
当一个引用可能为 null 值时, 对应的类型声明必须明确地标记为可为 null。
当 str 中的字符串内容不是一个整数时, 返回 null:
fun parseInt(str: String): Int? {
// ...
}
类型检测及自动类型转换
使用 is 运算符检测一个表达式是否某类型的一个实例(类似于Java中的instanceof关键字)
fun getStringLength(obj: Any): Int? {
if (obj is String) {
// 做过类型判断以后,obj会被系统自动转换为String类型
return obj.length
}
//在这里还有一种方法,与Java中instanceof不同,使用!is
// if (obj !is String){
// // XXX
// }
// 这里的obj仍然是Any类型的引用
return null
}
fun getStringLength(obj: Any): Int? {
if (obj !is String)
return null
// 在这个分支中, `obj` 的类型会被自动转换为 `String`
return obj.length
}
区间
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
// 使用 until 函数排除结束元素
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
基本数据类型
基本数字类型
Kotlin的基本数字类型包括 Byte、Short、Int、Long、Float、Double等:
类型 | 类型 |
---|---|
Double | 64 |
Float | 32 |
Long | 64 |
Int | 32 |
Short | 16 |
Byte | 8 |
下面是所有类型的字面常量:
- 十进制:123
- 长整型以大写的 L 结尾:123L
- 16 进制以 0x 开头:0x0F
- 2 进制以 0b 开头:0b00001011
注意:8进制不支持
Kotlin 同时也支持传统符号表示的浮点数值: - Doubles 默认写法: 123.5, 123.5e10
- Floats 使用 f 或者 F 后缀:123.5f
你可以使用下划线使数字常量更易读:
val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
比较两个数字
在 Kotlin 中,三个等号 === 表示比较对象地址,两个 == 表示比较两个值大小
fun main(args: Array<String>) {
val a: Int = 10000
println(a === a) // true,值相等,对象地址相等
//经过了装箱,创建了两个不同的对象
val boxedA: Int? = a
val anotherBoxedA: Int? = a
//虽然经过了装箱,但是值是相等的,都是10000
println(boxedA === anotherBoxedA) // false,值相等,对象地址不一样
println(boxedA == anotherBoxedA) // true,值相等
val a = 10000//不显示声明类型不会封装新对象
val boxedA = a
val anotherBoxedA = a
println(boxedA === anotherBoxedA) // true
}
类型转换
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b // 错误
val b: Byte = 1 // OK, 字面值是静态检测的
val i: Int = b.toInt() // OK
每种数据类型都有下面的这些方法,可以转化为其它的类型:
toByte(): Byte
toShort(): Short
toInt(): Int
toLong(): Long
toFloat(): Float
toDouble(): Double
toChar(): Char
位运算
对于Int和Long类型,还有一系列的位操作符可以使用,分别是:
- shl(bits) – 左移位 (Java’s «)
- shr(bits) – 右移位 (Java’s »)
- ushr(bits) – 无符号右移位 (Java’s »>)
- and(bits) – 与
- or(bits) – 或
- xor(bits) – 异或
- inv() – 反向
字符
Kotlin 中的 Char 不能直接和数字操作,Char 必需是单引号 ‘ 包含起来的。比如普通字符 ‘0’,’a’:
fun check(c: Char) {
if (c == 1) { // 错误:类型不兼容
// ……
}
}
字符字面值用单引号括起来: ‘1’。 特殊字符可以用反斜杠转义。 支持这几个转义序列:\t、 \b、\n、\r、'、"、\ 和 $。 编码其他字符要用 Unicode 转义序列语法:’\uFF00’。
我们可以显式把字符转换为 Int 数字:
fun decimalDigitValue(c: Char): Int {
if (c !in '0'..'9')
throw IllegalArgumentException("Out of range")
return c.toInt() - '0'.toInt() // 显式转换为数字
}
当需要可空引用时,像数字、字符会被装箱。装箱操作不会保留同一性。
布尔
布尔用 Boolean 类型表示,它有两个值:true 和 false。
若需要可空引用布尔会被装箱。
|| – 短路逻辑或
&& – 短路逻辑与
! - 逻辑非
数组
数组用类 Array 实现,并且还有一个 size 属性及 get 和 set 方法,由于使用 [] 重载了 get 和 set 方法,所以我们可以通过下标很方便的获取或者设置数组对应位置的值。
数组的创建两种方式:一种是使用函数arrayOf();另外一种是使用工厂函数。如下所示,我们分别是两种方式创建了两个数组:
fun main(args: Array<String>) {
//[1,2,3]
val a = arrayOf(1, 2, 3)
//[0,2,4]
val b = Array(3, { i -> (i * 2) })
//读取数组内容
println(a[0]) // 输出结果:1
println(b[1]) // 输出结果:2
}
如上所述,[] 运算符代表调用成员函数 get() 和 set()。
注意: 与 Java 不同的是,Kotlin 中数组是不型变的(invariant)。
除了类Array,还有ByteArray, ShortArray,IntArray,用来表示各个类型的数组,省去了装箱操作,因此效率更高,其用法同Array一样:
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]
字符串
和 Java 一样,String 是不可变的。
fun main(args: Array<String>) {
val text = """ //Kotlin 支持三个引号 """ 扩起来的字符串,支持多行字符串
|多行字符串
|多行字符串
"""
println(text) // 输出有一些前置空格
println(text.trimMargin()) // 前置空格删除了
//默认 | 用作边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")
//方括号 [] 语法可以很方便的获取字符串中的某个字符,也可以通过 for 循环来遍历
for (c in str) {
println(c)
}
}
字符串模板
fun main(args: Array<String>) {
val i = 10
val s = "i = $i" // 求值结果为 "i = 10"
println(s)
val s = "runoob"
val str = "$s.length is ${s.length}" // 求值结果为 "runoob.length is 6"
val price = """
${'$'}9.99
"""
println(price) // 求值结果为 $9.99
}
If条件控制
// 传统用法
var max = a
if (a < b) max = b
// 使用 else
var max: Int
if (a > b) {
max = a
} else {
max = b
}
// 作为表达式
val max = if (a > b) a else b
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}
When 表达式
//类似switch
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x 不是 1 ,也不是 2")
}
}
//如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}
//如果不提供参数,when 也可以用来取代 if-else if链
when {
x.isOdd() -> print("x is odd")
x.isEven() -> print("x is even")
else -> print("x is funny")
}
For 循环
for (item in collection) print(item)
for (item: Int in ints) {
// ……
}
//如果你想要通过索引遍历一个数组或者一个 list
for (i in array.indices) {
print(array[i])
}
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
while 与 do…while 循环
while( 布尔表达式 ) {
//循环内容
}
do {
//代码语句
}while(布尔表达式)
Kotlin 类
class Runoob { // 类名为 Runoob
// 大括号内是类体构成
}
//我们也可以定义一个空类
class Empty
class Runoob() {
fun foo() { print("Foo") } // 成员函数
}
类的属性
class Runoob {
var name: String = ……
var url: String = ……
var city: String = ……
}
val site = Runoob() // Kotlin 中没有 new 关键字
site.name // 使用 . 号来引用
site.url
//Koltin 中的类可以有一个主构造器,以及一个或多个次构造器,主构造器是类头部的一部分,位于类名称之后
class Person constructor(firstName: String) {}
//如果主构造器没有任何注解,也没有任何可见度修饰符,那么constructor关键字可以省略。
class Person(firstName: String) {}
var allByDefault: Int? // 错误: 需要一个初始化语句, 默认实现了 getter 和 setter 方法
var initialized = 1 // 类型为 Int, 默认实现了 getter 和 setter
val simple: Int? // 类型为 Int ,默认实现 getter ,但必须在构造函数中初始化
val inferredType = 1 // 类型为 Int 类型,默认实现 getter
//getter setter
class Person {
var no: Int = 100
get() = field // 后端变量
set(value) {
if (value < 10) { // 如果传入的值小于 10 返回该值
field = value
} else {
field = -1 // 如果传入的值大于等于 10 返回 -1
}
}
}
// 测试
fun main(args: Array<String>) {
var person: Person = Person()
person.no = 9
println("no:${person.no}")
person.no = 20
println("no:${person.no}")
}
类的构造器
class Person constructor(firstName: String) {
init {
println("FirstName is $firstName")
}
}
//可以通过主构造器来定义属性并初始化属性值(可以是var或val)
class People(val firstName: String, val lastName: String) {
//...
}
class Person {
constructor(parent: Person) { //次构造函数
parent.children.add(this)
}
}
class Person(val name: String) {
constructor (name: String, age:Int) : this(name) { //在同一个类中代理另一个构造函数使用 this 关键字
...
}
}
class DontCreateMe private constructor () {} //构造函数修饰符
/*在 JVM 虚拟机中,如果主构造函数的所有参数都有默认值,编译器会生成一个附加的无参的构造函数,这个构造函数会直接使用默认值。这使得 Kotlin 可以更简单的使用像 Jackson 或者 JPA 这样使用无参构造函数来创建类实例的库*/
class Customer(val customerName: String = "")
抽象类
open class Base {
open fun f() {}
}
abstract class Derived : Base() {
override abstract fun f()
}
嵌套类
class Outer { // 外部类
private val bar: Int = 1
class Nested { // 嵌套类
fun foo() = 2
}
}
fun main(args: Array<String>) {
val demo = Outer.Nested().foo() // 调用格式:外部类.嵌套类.嵌套类方法/属性
println(demo) // == 2
}
内部类
class Outer {
private val bar: Int = 1
var v = "成员属性"
/**嵌套内部类**/
inner class Inner {
fun foo() = bar // 访问外部类成员
fun innerTest() {
var o = this@Outer //获取外部类的成员变量
println("内部类可以引用外部类的成员,例如:" + o.v)
}
}
}
fun main(args: Array<String>) {
val demo = Outer().Inner().foo()
println(demo) // 1
val demo2 = Outer().Inner().innerTest()
println(demo2) // 内部类可以引用外部类的成员,例如:成员属性
}
匿名内部类
class Test {
var v = "成员属性"
fun setInterFace(test: TestInterFace) {
test.test()
}
}
/**
* 定义接口
*/
interface TestInterFace {
fun test()
}
fun main(args: Array<String>) {
var test = Test()
/**
* 采用对象表达式来创建接口对象,即匿名内部类的实例。
*/
test.setInterFace(object : TestInterFace {
override fun test() {
println("对象表达式创建匿名内部类的实例")
}
})
}
类的修饰符
classModifier: 类属性修饰符,标示类本身特性。
abstract // 抽象类
final // 类不可继承,默认属性
enum // 枚举类
open // 类可继承,类默认是final的
annotation // 注解类
accessModifier: 访问权限修饰符
private // 仅在同一个文件中可见
protected // 同一个文件中或子类可见
public // 所有调用的地方都可见
internal // 同一个模块中可见
类的继承
Kotlin 中所有类都继承该 Any 类,它是所有类的超类,对于没有超类型声明的类是默认超类, Any 默认提供了三个函数:
- equals()
- hashCode()
- toString()
class Example // 从 Any 隐式继承
open class Base(p: Int) // 如果一个类要被继承,可以使用 open 关键字进行修饰
class Derived(p: Int) : Base(p)
//子类有主构造函数
open class Person(var name : String, var age : Int){}// 基类
class Student(name : String, age : Int, var no : String, var score : Int) : Person(name, age) {}
//子类没有主构造函数
class Student : Person {
constructor(ctx: Context) : super(ctx) {
}
constructor(ctx: Context, attrs: AttributeSet) : super(ctx,attrs) {
}
}
重写
在基类中,使用fun声明函数时,此函数默认为final修饰,不能被子类重写。如果允许子类重写该函数,那么就要手动添加 open 修饰它, 子类重写方法使用 override 关键词
/**用户基类**/
open class Person{
open fun study(){ // 允许子类重写
println("我毕业了")
}
}
/**子类继承 Person 类**/
class Student : Person() {
override fun study(){ // 重写方法
println("我在读大学")
}
}
属性重写
可以用一个var属性重写一个val属性,但是反过来不行。因为val属性本身定义了getter方法,重写为var属性会在衍生类中额外声明一个setter方法
你可以在主构造函数中使用 override 关键字作为属性声明的一部分
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
----
open class Foo {
open val x: Int get { …… }
}
class Bar1 : Foo() {
override val x: Int = ……
}
Kotlin 接口
interface MyInterface {
var name:String //name 属性, 抽象的
fun bar() // 未实现
fun foo() { //已实现
// 可选的方法体
println("foo")
}
fun say() { print("say") } // 已实现
}
class Child : MyInterface {
override fun say() { super<MyInterface>.say() } // 重写
override var name: String = "runoob" //重载属性
override fun bar() {
// 方法体
}
}
扩展函数
Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式
class User(var name:String)
/**扩展函数**/
fun User.Print(){
print("用户名 $name")
}
fun main(arg:Array<String>){
var user = User("Runoob")
user.Print()
}
/* 扩展函数是静态解析的,并不是接收者类型的虚拟成员,在调用扩展函数时,具体被调用的的是哪一个函数,由调用函数的的对象表达式来决定的,而不是动态的类型决定的 */
open class C
class D: C()
fun C.foo() = "c" // 扩展函数 foo
fun D.foo() = "d" // 扩展函数 foo
fun printFoo(c: C) {
println(c.foo()) // 类型是 C 类
}
fun main(arg:Array<String>){
printFoo(D()) // => c
}
//若扩展函数和成员函数一致,则使用该函数时,会优先使用成员函数
class C {
fun foo() { println("成员函数") }
}
fun C.foo() { println("扩展函数") }
fun main(arg:Array<String>){
var c = C()
c.foo() // => 成员函数
}
//扩展一个空对象
fun Any?.toString(): String {
if (this == null) return "null"
// 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
// 解析为 Any 类的成员函数
return toString()
}
fun main(arg:Array<String>){
var t = null
println(t.toString())
}
扩展属性
val <T> List<T>.lastIndex: Int
get() = size - 1
//不允许被初始化,只能由显式提供的 getter/setter 定义
val Foo.bar = 1 // 错误:扩展属性不能有初始化器
伴生对象的扩展
类内部的对象声明可以用 companion 关键字标记,这样它就与外部类关联在一起,我们就可以直接通过外部类访问到对象的内部元素。
一个类里面只能声明一个内部关联对象,即关键字 companion 只能使用一次。
class MyClass {
companion object { } // 将被称为 "Companion"
}
fun MyClass.Companion.foo() {
println("伴随对象的扩展函数")
}
val MyClass.Companion.no: Int
get() = 10
fun main(args: Array<String>) {
println("no:${MyClass.no}")
MyClass.foo()
}
扩展的作用域
package foo.bar
fun Baz.goo() { …… }
---
package com.example.usage
import foo.bar.goo // 导入所有名为 goo 的扩展
import foo.bar.* // 从 foo.bar 导入一切
fun usage(baz: Baz) {
baz.goo()
}
扩展声明为成员
class D {
fun bar() { println("D bar") }
}
class C {
fun baz() { println("C baz") }
fun D.foo() {
bar() // 调用 D.bar
baz() // 调用 C.baz
}
fun caller(d: D) {
d.foo() // 调用扩展函数
}
}
fun main(args: Array<String>) {
val c: C = C()
val d: D = D()
c.caller(d) // D bar C baz
}
数据类
数据类关键字data, 编译器会自动的从主构造函数中根据所有声明的属性提取以下函数:
- equals() / hashCode()
- toString() 格式如 “User(name=John, age=42)”
- componentN() functions 对应于属性,按声明顺序排列
- copy() 函数
如果这些函数在类中已经被明确定义了,或者从超类中继承而来,就不再会生成。
为了保证生成代码的一致性以及有意义,数据类需要满足以下条件:
- 主构造函数至少包含一个参数。
- 所有的主构造函数的参数必须标识为val 或者 var ;
- 数据类不可以声明为 abstract, open, sealed 或者 inner;
- 数据类不能继承其他类 (但是可以实现接口)。
data class User(val name: String, val age: Int) //关键字为 data
fun copy(name: String = this.name, age: Int = this.age) = User(name, age) val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)
println(jack) // => User(name=Jack, age=1)
println(olderJack) // => User(name=Jack, age=2)
密封类
使用 sealed 修饰类,密封类可以有子类,但是所有的子类都必须要内嵌在密封类中。
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun eval(expr: Expr): Double = when (expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
泛型
class Box<T>(t: T) {
var value = t
}
//创建类的实例时我们需要指定类型参数
val box: Box<Int> = Box<Int>(1)
// 或者
val box = Box(1) // 编译器会进行类型推断,1 类型 Int,所以编译器知道我们说的是 Box<Int>。
fun main(args: Array<String>) {
val age = 23
val name = "runoob"
val bool = true
doPrintln(age) // 整型
doPrintln(name) // 字符串
doPrintln(bool) // 布尔型
}
fun <T> doPrintln(content: T) {
when (content) {
is Int -> println("整型数字为 $content")
is String -> println("字符串转换为大写:${content.toUpperCase()}")
else -> println("T 不是整型,也不是字符串")
}
}
//泛型约束
fun <T : Comparable<T>> sort(list: List<T>) {
// ……
}
sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子类型
sort(listOf(HashMap<Int, String>())) // 错误:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子类型
// 明处的类型变异使用协变注解修饰符:in、out,消费者 in, 生产者 out。
// 使用 out, 协变类型参数只能用作输出,可以作为返回值类型但是无法作为入参的类型
class Runoob<out A>(val a: A) {
fun foo(): A {
return a
}
}
fun main(args: Array<String>) {
var strCo: Runoob<String> = Runoob("a")
var anyCo: Runoob<Any> = Runoob<Any>("b")
anyCo = strCo
println(anyCo.foo()) // 输出 a
}
// in 使得一个类型参数逆变,逆变类型参数只能用作输入,可以作为入参的类型但是无法作为返回值的类型
class Runoob<in A>(a: A) {
fun foo(a: A) {
}
}
fun main(args: Array<String>) {
var strDCo = Runoob("a")
var anyDCo = Runoob<Any>("b")
strDCo = anyDCo
}
//如果类型定义为interface Function<in T, out U> , 那么可以出现以下几种星号投射
Function<*, String> , 代表 Function<in Nothing, String> ;
Function<Int, *> , 代表 Function<Int, out Any?> ;
Function<, > , 代表 Function<in Nothing, out Any?> .
Kotlin 枚举类
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
enum class ProtocolState {
WAITING {
override fun signal() = TALKING
},
TALKING {
override fun signal() = WAITING
};
abstract fun signal(): ProtocolState
}
enum class RGB { RED, GREEN, BLUE }
//可以使用 enumValues<T>() 和 enumValueOf<T>() 函数以泛型的方式访问枚举类中的常量
inline fun <reified T : Enum<T>> printAllValues() {
print(enumValues<T>().joinToString { it.name })
}
fun main(args: Array<String>) {
printAllValues<RGB>() // 输出 RED, GREEN, BLUE
var color:Color=Color.BLUE
println(Color.values()) // 以数组的形式,返回枚举值
println(Color.valueOf("RED"))
//转换指定 name为枚举值,若未匹配成功,会抛出IllegalArgumentException
println(Color.valueOf("RED"))
println(color.name) //获取枚举名称
println(color.ordinal) //获取枚举值在所有枚举数组中定义的顺序
}
Kotlin 对象表达式和对象声明
class C {
// 私有函数,所以其返回类型是匿名对象类型
private fun foo() = object {
val x: String = "x"
}
// 公有函数,所以其返回类型是 Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // 没问题
val x2 = publicFoo().x // 错误:未能解析的引用“x”
}
}
//作用域
fun countClicks(window: JComponent) {
var clickCount = 0
var enterCount = 0
window.addMouseListener(object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent) {
clickCount++
}
override fun mouseEntered(e: MouseEvent) {
enterCount++
}
})
// ……
}
//以下实例中,两个对象都输出了同一个 url 地址
object Site {
var url:String = ""
val name: String = "菜鸟教程"
}
fun main(args: Array<String>) {
var s1 = Site
var s2 = Site
s1.url = "www.runoob.com"
println(s1.url)
println(s2.url)
}
kotlin 委托
// 创建接口
interface Base {
fun print()
}
// 实现此接口的被委托的类
class BaseImpl(val x: Int) : Base {
override fun print() { print(x) }
}
// 通过关键字 by 建立委托类
class Derived(b: Base) : Base by b
fun main(args: Array<String>) {
val b = BaseImpl(10)
Derived(b).print() // 输出 10
}
---
import kotlin.reflect.KProperty
// 定义包含属性委托的类
class Example {
var p: String by Delegate()
}
// 委托的类
class Delegate {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, 这里委托了 ${property.name} 属性"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
println("$thisRef 的 ${property.name} 属性赋值为 $value")
}
}
fun main(args: Array<String>) {
val e = Example()
println(e.p) // 访问该属性,调用 getValue() 函数
e.p = "Runoob" // 调用 setValue() 函数
println(e.p)
}
class C {
var prop: Type by MyDelegate()
}
// 这段是由编译器生成的相应代码:
class C {
private val prop$delegate = MyDelegate()
var prop: Type
get() = prop$delegate.getValue(this, this::prop)
set(value: Type) = prop$delegate.setValue(this, this::prop, value)
}
延迟属性 Lazy
val lazyValue: String by lazy {
println("computed!") // 第一次调用输出,第二次调用不执行
"Hello"
}
fun main(args: Array<String>) {
println(lazyValue) // 第一次执行,执行两次输出表达式
println(lazyValue) // 第二次执行,只输出返回值
}
可观察属性 Observable
import kotlin.properties.Delegates
class User {
var name: String by Delegates.observable("初始值") {
prop, old, new ->
println("旧值:$old -> 新值:$new")
}
}
fun main(args: Array<String>) {
val user = User()
user.name = "第一次赋值"
user.name = "第二次赋值"
}
=> 旧值:初始值 -> 新值:第一次赋值
=> 旧值:第一次赋值 -> 新值:第二次赋值
把属性储存在映射中
class Site(val map: Map<String, Any?>) {
val name: String by map
val url: String by map
}
fun main(args: Array<String>) {
// 构造函数接受一个映射参数
val site = Site(mapOf(
"name" to "菜鸟教程",
"url" to "www.runoob.com"
))
// 读取映射值
println(site.name)
println(site.url)
}
Not Null
//notNull 适用于那些无法在初始化阶段就确定属性值的场合。
class Foo {
var notNullBar: String by Delegates.notNull<String>()
}
foo.notNullBar = "bar"
println(foo.notNullBar) //需要注意,如果属性在赋值前就被访问的话则会抛出异常。
局部委托属性
class MyReadOnlyPropertyImpl : ReadOnlyProperty<MyTestClass, String> {
override fun getValue(thisRef: MyTestClass, property: KProperty<*>): String {
val s = "aaa"
return s;
}
}
class MyProvider {
operator fun provideDelegate(thisRef: MyTestClass, prop: KProperty<*>): ReadOnlyProperty<MyTestClass, String> {
println("do something") // 这行代码是在getValue方法之外调用的
val myReadOnlyPropertyImpl = MyReadOnlyPropertyImpl() // 这里才是调用getValue方法的地方
return myReadOnlyPropetyImpl
}
}
class MyTestClass {
val myProvider = MyProvider()
val myField1: String by myProvider
}
// 测试
fun main(args: Array<String>) {
val myTestClass = MyTestClass()
println(myTestClass.myField1)
}
=> do something
=> aaa
kotlin
- 在kotlin中各种map, filter其实都是使用的for(item in list)的方式
- Calling multiple methods on an object instance (‘with’)
val myTurtle = Turtle() with(myTurtle) { //draw a 100 pix square penDown() for(i in 1..4) { forward(100.0) turn(90.0) } penUp() }