Kotlin入门到放弃 零、Kotlin是什么? Kotlin就是一门可以运行在Java虚拟机、Android、浏览器上的静态语言,它与Java100%兼容,如果你对Java非常熟悉,那么你就会发现Kotlin除了自己的标准库之外,大多仍然使用经典的Java集合框架。
一、基本数据类型 1. byte类型定义 1 2 3 4 5 val byte: Byte = 1 val byte = 1
2. short类型定义 1 2 3 val short: Short = 2 val short = 2
3. char类型定义 1 2 3 val char: Char = 'A' val char = 'A'
4. int类型定义 1 2 3 val number: Int = 668 val number = 668
5. float类型定义 1 2 3 val float: Float = 0.5f val float = 0.5f
6. double类型定义 1 2 3 val double: Double = 0.5 val double = 0.5
7. long类型定义 1 2 3 val long: Long = 123456 val long = 123456
8. boolean类型定义 1 2 3 val bool: Boolean = true val bool = true
二、引用数据类型 1 2 3 4 5 val str: String = "Kotlin Day01" val str = "Kotlin Day01"
三、常量和变量
val:定义常量,不可修改的
var:定义变量,可以修改的
四、编译时常量和运行时常量
val:运行时常量(类似于Java中的final)
const val:编译时常量
Java的final:不可修改,编译时会把引用的值进行复制(编译时能确切知道赋值的具体内容)
Kotlin的val:不可修改,编译时不会把引用的值进行复制,属于运行时常量(因为编译时不能确切知道赋值的具体内容,故而不能进行赋值);而在val前面加const,就相当于Java中的final,编译时会把引用的值进行复制,属于编译时常量(编译时能确切知道赋值的具体内容)
1 2 const val TAG = "MainActivity"
五、函数的定义 1 2 3 4 5 6 7 8 9 fun add (number1:Int , number2:Int ) :Int { return number1 + number2 } 等价于:fun add (number1:Int , number2:Int ) = number1 + number2
六、函数变量(匿名函数) 在Kotlin中函数可以作为变量,也可以作为参数传递。
1 2 3 4 5 6 7 8 9 val calc = fun (number1:Int , number2:Int ) :Int { return number1 - number2 } 等价于:val calc = fun (number1:Int , number2:Int ) = number1 - number2
七、Lambda初探 Lambda表达式就是一个匿名函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 val calcLambda = { number1:Int , number2:Int -> number1 - number2 }val printHello = { println("Hello World" ) }val sum = { arg1:Int , arg2:Int -> println("$arg1 + $arg2 = ${arg1 + arg2} " ) arg1 + arg2 } sum(1 ,3 ) sum.invoke(1 ,3 )
注意:在Lambda表达式return,是return整个方法体。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 fun main (args: Array <String >) { args.forEach { if (it == "1" ) return println(it) } println("The End" ) }fun main (args: Array <String >) { args.forEach ForEach@ { if (it == "1" ) return @ForEach println(it) } println("The End" ) }
Lambda表达式的简化
函数参数调用时最后一个Lambda可以移出去
函数参数只有一个Lambda,调用时小括号可省略
Lambda只有一个参数可默认为it
入参、返回值与形参一致的函数可以用函数引用的方式作为实参传入,也就是使用 ::
八、字符串模板
取值
1 2 3 4 val str = "number = $number "
无缝拼接
1 2 3 4 val strAppend = "number = ${ number } 12121"
转义
1 2 3 4 val strTransfer = "\$number"
九、类型转换 1 2 var numberStr = "12" val numberInt = numberStr.toInt()
十、equals 和 == 以及 ===
十一、空安全 任何对象都可分为可空和不可空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fun token () :String? { return null }val token = token()if (token != null ) { val length = token.length }f 或者val length = token()!!.length 亦或者val length = token()?.length 还可以 token()?: return
1 2 3 4 5 6 7 8 fun token () :String { return "" }val length = token().length
十二、数组(Array) 1 2 val intArr = intArrayOf(1 ,2 ,3 ,4 ,5 ) val strArr = arrayOf("str1" , "str2" , "str3" )
1. 遍历数组 1 2 3 4 val strArr = arrayOf("str1" , "str2" , "str3" )for (str in strArr) { System.out .println(str) }
2.角标遍历数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 val strArr = arrayOf("str1" , "str2" , "str3" )for (index in strArr.indices) { System.out .println(strArr[index]) } 或者for (index in IntRange(0 , strArr.size-1 )) { System.out .println(strArr[index]) } 或者for (index in 0. .strArr.size-1 ) { System.out .println(strArr[index]) } 或者for (index in 0 until strArr.size) { System.out .println(strArr[index]) }
十三、区间(Range)
一个数学上的概念,表示范围
ClosedRange的子类,IntRange最常用
基本写法:
0..100 表示[0, 100]
0 until 100 表示[0, 100)
index in 0..100 判断index是否在[0, 100]中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 val intRange = IntRange(0 , strArr.size) for (index in intRange) { Log.e(TAG, "index=" + index) } 或者val intRange = 0. .strArr.size for (index in intRange) { Log.e(TAG, "index=" + index) } 或者val intRange = 0 until strArr.size for (index in intRange) { Log.e(TAG, "index=" + index) }
十四、Lambda再探 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 strArr.filter ({ element -> element.isNotEmpty() }).forEach { e(it) } strArr.filter (){ element -> element.isNotEmpty() }.forEach { e(it) } strArr.filter { element -> element.isNotEmpty() }.forEach { e(it) } strArr.filter { it.isNotEmpty() }.forEach { e(it) } strArr.filter { it.isNotEmpty() }.forEach (::e) fun e (message: String ) { Log.e(TAG, message) }
十五、when表达式 相当于Java版的switch case
1 2 3 4 5 6 7 8 9 10 11 12 13 14 val num = 1 when (num) { 1 -> Log.e(TAG, "is 1" ) is Int -> Log.e(TAG, "is Int" ) in 1. .3 -> Log.e(TAG, "in 1..3" ) !in 1. .3 -> Log.e(TAG, "not in 1..3" ) }val numStr = when (num) { 1 -> "1" 2 -> "2" else -> "" } Log.e(TAG, "numberStr = $numStr " )
十六、类的创建 所有类都最终继承自Any
1 2 3 4 5 6 7 class Person (val name:String, val age:Int )val person = Person("Zane" , 26 )class Person (var name:String, var age:Int )
十七、构造函数重载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Person (val name:String?, val age:Int ) { constructor (name:String):this (name, 0 ) constructor ():this (null , 0 ) }val person1 = Person()val person2 = Person("Hello World" )val person = Person("Zane" , 26 )class Person { var name:String? = null var age:Int = 0 constructor () constructor (name:String) { this .name =name } constructor (name:String, age:Int ) { this .name = name this .age = age } }val person1 = Person()val person2 = Person("Hello World" )val person = Person("Zane" , 26 )
十八、类的成员 1 2 3 4 class Person { var age:Int = 0 var name:String? = null }
定义的属性,默认的情况下会拥有get和set方法 ,如果想私有化属性的get和set方法:
1 2 3 4 5 private var name:String? = null private set private get
示例:判断传参的年龄是否大于0,如果小于0,返回0,否则直接返回
1 2 3 4 var age:Int = 0 get () { return if (field < 0 ) 0 else field }
属性初始化:
十九、类的继承
父类需要open才可以被继承
父类方法、属性需要open才可以被覆写
接口、接口方法、抽象类默认就为open
覆写父类(接口)成员需要override关键字
子类(实现类)继承(实现)父类(接口),使用 :
继承类时实际上调用了父类构造方法
类只能单继承,接口可以多实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 open class Person { var age:Int = 0 var name:String? = null constructor (name:String) { this .name =name } }class Student (name:String) : Person(name) { }val student = Student("Zane" ) student.name = "hello world" student.age = 26
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class QQStepView : View { constructor (context: Context):this (context, null ) constructor (context: Context, attributes: AttributeSet?):this (context, attributes, 0 ) constructor (context: Context, attributes: AttributeSet?, defStyle: Int ):super (context, attributes, defStyle) { } override fun onDraw (canvas: Canvas ?) { super .onDraw(canvas) Log.e("QQStepView" , "onDraw" ) } } <io.github.kotlin.day01.QQStepView android:layout_width="match_parent" android:layout_height="match_parent" />
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 abstract class PersonKt (open val age:Int ) { abstract fun work () }class Programmer (age: Int ):PersonKt(age) { override val age: Int get () { return 0 } override fun work () { println("我是码农,我在搬砖..." ) } }class Doctor (age: Int ): PersonKt(age) { override fun work () { println("我是医生,我在动手术..." ) } }fun main (args: Array <String >) { val programmer:PersonKt = Programmer(23 ) programmer.work() println(programmer.age) val doctor:PersonKt = Doctor(24 ) doctor.work() println(doctor.age) }
接口代理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 interface Driver { fun driver () }interface Writer { fun writer () }class CarDriver : Driver { override fun driver () { println("开车中..." ) } }class PPTWriter : Writer { override fun writer () { println("写PPT中..." ) } }class SeniorManger (val driver: Driver, val writer: Writer): Driver by driver, Writer by writerfun main (args: Array <String >) { val carDriver = CarDriver() val pptWriter = PPTWriter() val seniorManger = SeniorManger(carDriver, pptWriter) seniorManger.driver() seniorManger.writer() }
接口方法冲突:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 interface B { fun x () :Int = 0 }interface C { fun x () :Int = 1 }abstract class D { open fun x () = 2 }class E (val y:Int = 0 ): B,C,D() { override fun x () : Int { println("call x():Int in E" ) if (y > 0 ) { return y }else if (y < -200 ) { return super <C>.x() }else if (y < -100 ) { return super <B>.x() }else { return super <D>.x() } } }fun main (args: Array <String >) { println(E(3 ).x()) println(E(-10 ).x()) println(E(-110 ).x()) println(E(-10000 ).x()) }
二十、接口与方法重载
接口,直观理解就是一种约定
不能有状态
必须由类对其进行实现后使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 interface Callback { fun onError (e: IOException ) fun onSuccess (resultJson: String ) { Log.e("Callback:" , "onSuccess" ) } }class HttpCallback : Callback { override fun onSuccess (resultJson: String ) { } override fun onError (e: IOException ) { } }
二十一、抽象类
实现了一部分协议的半成品
可以有状态,可以有方法实现
必须由子类继承后使用
1 2 3 4 5 6 7 8 9 10 abstract class HttpCallback : Callback { override fun onSuccess (resultJson: String ) { } abstract fun onSuccess () }
抽象类和接口的共性:
比较抽象,不能直接实例化
有需要子类(实现类)实现的方法
父类(接口)变量可以接受子类(实现类)的实例赋值
抽象类和接口的区别:
抽象类有状态,接口没有状态
抽象类有方法实现,接口只能有无状态的默认实现
抽象类只能单继承,接口可以多实现
抽象类反映本质,接口体现能力
二十二、匿名内部类
Kotlin里的匿名内部类,可以继承一个类,实现多个接口
没有定义名称的内部类,但类名编译时会生成,类似 Outter$1.class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 open class Test interface Callback { fun onError (e: IOException ) fun onSuccess (resultJson: String ) }abstract class HttpCallback : Callback { override fun onSuccess (resultJson: String ) { onSuccess() } abstract fun onSuccess () }class HttpUtil { fun get (callback: Callback ) { callback.onSuccess("成功" ) } }val httpUtil = HttpUtil() httpUtil.get (object : HttpCallback(){ override fun onSuccess () { Log.e(TAG, "onSuccess" ) } override fun onError (e: IOException ) { } }) httpUtil.get (object : Test(), HttpCallback(){ override fun onSuccess () { Log.e(TAG, "onSuccess" ) } override fun onError (e: IOException ) { } })
二十三、默认参数
为函数参数指定默认值
可以为任意位置的参数指定默认值,但传参时出现歧义,需要使用具名参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class HttpUtil { fun get (callback: Callback , url:String , cache:Boolean = false ) { callback.onSuccess("成功" ) } }val httpUtil = HttpUtil() httpUtil.get (object : HttpCallback(){ override fun onSuccess () { Log.e(TAG, "onSuccess" ) } override fun onError (e: IOException ) { } },"https://www.baidu.com" )
温馨提示:如果默认参数指定得靠前的话,后面的参数就必须使用具名参数进行传值!
1 2 3 4 5 6 7 8 fun hello (double: Double = 3.0 , vararg ints:Int , string: String ) { println(double) ints.forEach( ::println ) println(string) }val array = intArrayOf(1 ,2 ,3 ,4 ) hello(ints=*array,string="1" )
二十四、具名参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class HttpUtil { fun get (callback: Callback , url:String = "https://www.baidu.com" , cache:Boolean ) { callback.onSuccess("成功" ) } }val httpUtil = HttpUtil() httpUtil.get (object : HttpCallback(){ override fun onSuccess () { Log.e(TAG, "onSuccess" ) } override fun onError (e: IOException ) { } },cache = true )
二十五、可变参数
某个参数可以接收多个值
可以不为最后一个参数
如果传参时有歧义,需要使用具名参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class CalcUtil { fun add (vararg nums:Int ) :Int { var sum = 0 nums.forEach { sum += it } return sum } }val calcUtil = CalcUtil()val total = calcUtil.add(1 ,2 ,3 ,4 )
Spread Operator:
二十六、运算符重载 参考文档:https://kotlinlang.org/docs/operator-overloading.html
1 2 3 4 5 6 7 8 9 10 11 12 class Counter (val dayIndex: Int ) { operator fun plus (increment: Int ) : Counter { return Counter(dayIndex + increment) } operator fun minus (reduce: Int ) : Counter { return Counter(dayIndex - reduce) } }
1 2 3 4 5 val counter1 = Counter(2 )val counter2 = Counter(6 )val counter = counter1 + counter2.dayIndexval counterReduce = counter1 - counter2.dayIndex
任何类可以定义或者重载父类的基本运算符
通过运算符对应的具名函数来定义
对参数个数做要求,对参数和返回值类型不做要求
不能像Scala一样定义任意运算符
二十七、伴生对象与静态成员
每个类可以对应一个伴生对象
伴生对象的成员全局独一份
伴生对象的成员类似 Java 的静态成员
静态成员考虑用包级函数、包级变量替代
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Util { companion object { val baseUrl = "www.baidu.com" fun add (vararg nums:Int ) :Int { var sum = 0 nums.forEach { sum += it } return sum } } }val total = Util.add(1 ,2 ,3 )val url = Util.baseUrl
如何在Java类中不调用Companion,也能直接调用静态成员呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Util { companion object { @JvmField val baseUrl = "www.baidu.com" @JvmStatic fun add (vararg nums:Int ) :Int { var sum = 0 nums.forEach { sum += it } return sum } } }public class StaticJava { public static void main(String[] args) { int sum = Util.add(1 ,2 ,3 ); System.out .println(Util.baseUrl); } }
二十八、类成员扩展 1 2 3 val arrs = arrayOf(1 ,2 ,3 ,4 ) arrs.isNotEmpty()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 strContent.mulit(3 )fun String.mulit (number: Int ) :String { val sb = StringBuilder() for (num in 1. .number) { sb.append(this ) } return sb.toString() }operator fun String.times (number: Int ) :String { val sb = StringBuilder() for (num in 1. .number) { sb.append(this ) } return sb.toString() }val String.str: String get () = "abc" "aaa" .str
二十九、内部类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Outer { val name = "Zane_Outer" inner class Inner { val name = "Zane_Inner" fun printName () { Log.e("Inner" , "name = ${this@Outer.name} " ) } } } Outer().Inner().printName()
三十、枚举
实例可数的类,注意枚举也是类
可以修改构造,添加成员
可以提升代码的表现力,也有一定的性能开销
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 enum class LogLevel { VERBOSE,DEBUG,INFO,WARN,ERROR,ASSERT }class LogLevel2 protected constructor () { companion object { val VERBOSE= LogLevel2() val DEBUG= LogLevel2() val INFO= LogLevel2() val WARN= LogLevel2() val ERROR= LogLevel2() val ASSERT= LogLevel2() } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 enum class LogLevel (val tag:Int ) { VERBOSE(0 ),DEBUG(1 ),INFO(2 ),WARN(3 ),ERROR(4 ),ASSERT(5 ); fun getTag () :String { return "$tag " } override fun toString () : String { return "$tag , $name " } }fun main (args: Array <String >) { println(LogLevel.DEBUG.getTag()) println(LogLevel.DEBUG.ordinal) LogLevel.values().map(::println) println(LogLevel.valueOf("ERROR" )) }
三十一、智能类型转换 1 2 3 4 val person: Person = Student("zane" )if (person is Student) { person.getStudentName() }
1 2 3 4 val str1:String? = "zane" if (str1 != null ) { System.out .println(str1.length) }
1 2 3 val person: Person = Person("zane" )val student:Student? = person as ? Student
三十二、中缀表达式
只有一个参数,且用infix修饰的函数
可以允许不用 “对象.xx()” 去调用方法
1 2 3 4 5 6 class Book { infix fun on (place: String ) {...} } Book() on "My Desk"
温馨提示:如果想自定义运算符,可采用中缀表达式,但不可乱用哟~
三十三、分支表达式(if) 1 2 3 4 5 6 7 8 9 10 private const val DEBUG = 1 private const val USER = 0 fun main (args: Array <String >) { val mode = if (args.isNotEmpty() && args[0 ] == "1" ) { DEBUG }else { USER } }
三十四、循环语句
for循环
1 2 3 4 5 6 7 8 9 10 11 for (arg in args) { println(arg) }for ((index, value) in args.withIndex()) { println("$index -> $value " ) }for (indexAndValue in args.withIndex()) { println("${indexAndValue.index} -> ${indexAndValue.value} " ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class MyIterator (val iterator: Iterator<Int >) { operator fun next () :Int { return iterator.next() } operator fun hasNext () :Boolean { return iterator.hasNext() } }class MyIntList () { private val list = ArrayList<Int >() fun add (value:Int ) { list.add(value) } fun remove (value:Int ) { list.remove(value) } operator fun iterator () :MyIterator { return MyIterator(list.iterator()) } }fun main (args: Array <String >) { val list = MyIntList() list.add(1 ) list.add(2 ) list.add(3 ) for (i in list) { println(i) } }
while循环
1 2 3 4 5 6 7 8 9 10 11 12 var x = 15 while (x > 0 ) { println(x) x-- }do { println(x) x-- }while (x > 0 )
continue和break
1 2 3 4 5 6 7 8 9 for (arg in args) { if (arg == "5" ) continue if (arg == "10" ) break }Outter@ for (...){ Inner@ while (i<0 ){ if (...)break @Outter } }
三十五、异常捕获(try、catch、finally) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 try { val arg0 = args[0 ].toInt() val arg1 = args[1 ].toInt() println("$arg0 + $arg1 = ${arg0 + arg1} " ) }catch (e: NumberFormatException) { println("您确定输入的参数为整数吗?" ) }catch (e: ArrayIndexOutOfBoundsException) { println("您确定输入的是两个整数吗?" ) }catch (e: Exception) { println("未知错误!" ) }finally { println("感谢您使用加法计算器!" ) }
try、catch也是一个表达式:
1 2 3 4 5 6 7 val result = try { args[0 ].toInt() / args[1 ].toInt() }catch (e:Exception) { e.printStackTrace() 0 } println(result)
小结(计算器示例) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 class Operator (op:String) { val onFun:(left: Double , right: Double ) -> Double init { onFun = when (op) { "+" -> {l,r -> l + r} "-" -> {l,r -> l - r} "*" -> {l,r -> l * r} "/" -> {l,r -> l / r} "%" -> {l,r -> l % r} else -> { throw UnsupportedOperationException(op) } } } operator fun invoke (left: Double , right: Double ) : Double { return onFun(left, right) } }fun main (args: Array <String >) { while (true ) { println("请输入需要计算的内容,如3 + 4,运算符之间使用空格分隔哟~" ) try { val input = readLine()?:break val splits = input.trim().split(" " ) if (splits.size != 3 ) { throw IllegalArgumentException("参数个数不对" ) } val arg1 = splits[0 ].toDouble() val op = splits[1 ] val arg2 = splits[2 ].toDouble() println("$arg1 $op $arg2 = ${Operator(op)(arg1, arg2)} " ) }catch (e: IllegalArgumentException) { println("应输入三个参数的数字,且用空格分割!" ) }catch (e: UnsupportedOperationException) { println("暂不支持该运算:${e.message} " ) }catch (e: NumberFormatException) { println("您确定输入的是数字?" ) }catch (e: Exception) { println("未知异常!" ) } println("是否继续?[Y]" ) val cmd = readLine() if (cmd == null || cmd.toLowerCase() != "y" ) { break } } println("欢迎再次使用!" ) }
三十六、导出可执行程序 在项目build.gradle文件中,添加如下代码:
1 2 apply plugin:'application' mainClassName = "io.github.kotlin.day01.CalcDemoKt"
然后重新build一下,打开右侧Gradle控制板!我TM不想说了…继续下一个知识点!
三十七、类及其成员的可见性(private、protected、internal、public)
Kotlin
Java
private
private
protected
protected
-
default(包内可见)
internal(模块内可见)
-
public
public
三十八、Object关键字
只有一个实例的类
不能自定义构造方法
可以实现接口、继承分类
本质上就是单例模式最基本的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 interface onExternalDriverMountListener { fun onMount (driver: Driver ) fun onUnMount (driver:Driver ) }abstract class Player object MusicPlayer:onExternalDriverMountListener, Player() { val state = 0 fun player (url: String ) { } fun stop () { } override fun onMount (driver: Driver ) { } override fun onUnMount (driver: Driver ) { } }
三十九、包级函数
Kotlin允许不在类中定义的成员属性和方法,称为包级函数/对象
1 2 3 4 5 6 7 8 9 val packStr = "包级函数" fun packFun () :String { return packStr }class Util { }
Java 怎么调用 Kotlin 的包级对象呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 @file:JvmName ("PackKotlin" )package io.github.kotlin.day01val packStr = "包级函数" fun packFun () :String { return packStr }class Util { }
1 2 3 4 5 6 7 public class StaticJava { public static void main(String[] args) { PackKotlin.getPackStr(); PackKotlin.packFun(); } }
四十、方法重载(Overloads)与默认参数
方法重载:方法名相同、参数类型和个数不同的方法
Jvm函数签名的概念:与函数名、参数列表有关,与返回值类型无关
返回值类型不能作为签名的一部分,因此不能定义方法名相同但返回值类型不同的方法
方法重载与默认参数,二者可相互转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class OverLoad { fun a () :Int { return 0 } fun a (int: Int ) :Int { return int } fun a (int1: Int , int2:Int ) :Int { return int1 + int2 } fun a (string: String ) :Int { return string.length } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class OverLoad { fun a (int: Int = 0 ) :Int { return int } }fun main (args: Array <String >) { val overLoad = OverLoad() overLoad.a() } 但如果想在Java类中使用Kotlin的默认参数,则需要添加 @JvmOverloads 注解class OverLoad { @JvmOverloads fun a (int: Int = 0 ) :Int { return int } } OverLoad overLoad = new OverLoad(); overLoad.a();
四十一、属性代理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Delegates { val hello1 by lazy { "Hello1" } val hello2 by X() var hello3 by X() }class X { private var value:String? = null operator fun getValue (thisRef: Any ?, property: KProperty <*>) : String { println("getValue:$thisRef -> $property " ) return value?:"" } operator fun setValue (thisRef: Any ?, property: KProperty <*>, value: String ) { println("setValue:$thisRef -> $property -> $value " ) this .value = value } }fun main (args: Array <String >) { val delegates = Delegates() println(delegates.hello1) println(delegates.hello2) println(delegates.hello3) delegates.hello3 = "hello3" println(delegates.hello3) }
四十二、数据类(data class)
默认实现了 copy、toString等方法
默认实现了componentN方法
1 val/var (参数1 ...参数N ) = 实例对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 data class Country (val code:Int , val name:String)class ComponentX () { operator fun component1 () :String { return "您好,我是" } operator fun component2 () :Int { return 1 } operator fun component3 () :Int { return 1 } operator fun component4 () :Int { return 0 } }fun main (args: Array <String >) { val country = Country(84 , "中国" ) println(country) println(country.component1()) println(country.component2()) val (code, name) = country println(code) println(name) val componentX = ComponentX() val (a,b,c,d) = componentX println("$a $b $c $d " ) }
在Kotlin中,一般都将data class当作Java中的java bean,但是data class有两个问题:
1、定义一个data class的类,但它默认就是通过final关键字修饰的,这将造成data class类无法被子类继承
2、data class 的类,它默认就没有无参的构造方法
为了解决上述两个问题,官方推出了两款插件:allOpen,noArg插件
如何使用这两款插件:
1、首先在项目级的build.gradle文件中,添加如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 buildscript { ... dependencies { ... classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version " classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version " } } apply plugin:'kotlin-noarg' apply plugin:'kotlin-allopen' noArg{ annotation ("io.github.kotlin.day01.annotation.Poko" ) } allOpen{ annotation ("io.github.kotlin.day01.annotation.Poko" ) }
2、在项目中,新建annotation包,并创建Poko类(可自定义类名)
3、重新Rebuild Project
四十三、密封类(sealed class)
密封类是子类可数(有限)的类
kotlin v1.1版本之前,子类必须定义为密封类的内部类;v1.1版本之后,子类只需要与密封类在同一个文件中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 sealed class PlayerCmd { class Play (val url:String, val position:Long ): PlayerCmd() class Seek (val position: Long ): PlayerCmd() object Pause: PlayerCmd() object Resume: PlayerCmd() object Stop: PlayerCmd() } 或者sealed class PlayerCmd { }class Play (val url:String, val position:Long ): PlayerCmd()class Seek (val position: Long ): PlayerCmd()object Pause: PlayerCmd()object Resume: PlayerCmd()object Stop: PlayerCmd()
四十四、高阶函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 fun main (args: Array <String >) { val list = listOf(1 ,3 ,4 ,5 ,10 ,6 ,2 ) val newList = ArrayList<Int >() list.forEach { val newElement = it * 2 + 3 newList.add(newElement) } newList.forEach(::println) val list = listOf(1 ,3 ,4 ,5 ,10 ,6 ,2 ) val newList = list.map { it * 2 + 3 } val newList2 = list.map(Int ::toDouble) list.map(::println) newList.forEach(::println) newList2.forEach(::println) val list = listOf( 1. .20 , 2. .5 , 100. .300 ) val flatList = list.flatMap{ it } flatList.forEach(::println) val list = listOf( 1. .20 , 2. .5 , 100. .300 ) val flatList = list.flatMap{ it.map { "No:$it " } } val flatList2 = list.flatMap { intRange -> intRange.map {intElement -> "No:$intElement " } } flatList.forEach(::println) flatList2.forEach(::println) val list = listOf( 1. .20 , 2. .5 , 100. .322 ) val flatList = list.flatMap { it } println(flatList.reduce{acc, i -> acc + i}) (0. .6 ).map{it -> factorial(it)}.forEach{it -> println(it)} (0. .6 ).map(::factorial).forEach(::println) (0. .6 ).map(::factorial).reduce { acc, i -> acc + i } (0. .6 ).map(::factorial).fold(5 ){ acc, i -> acc + i} (0. .6 ).map(::factorial).fold(StringBuilder()){ acc, i -> acc.append(i).append("," ) } (0. .6 ).joinToString("," ) (0. .6 ).map(::factorial).foldRight(StringBuilder()) { i, acc -> acc.append(i).append("," ) } (0. .6 ).map(::factorial).filter { i -> i % 2 ==1 } (0. .6 ).map(::factorial).filterIndexed{ index,i -> index % 2 == 1 } (0. .60 ).map(::factorial).takeWhile { it % 2 == 1 } }fun factorial (n: Int ) :Int { if (n == 0 ) return 1 return (1. .n).reduce{acc, i -> acc * i } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 data class PersonData (val name:String, val age:Int )fun findPerson () : PersonData? { return null }fun main (args: Array <String >) { findPerson()?.let { (name, age) -> println(name) println(age) } }data class PersonData (val name:String, val age:Int ) { fun work () { println("$name is working!" ) } }fun findPerson () : PersonData? { return PersonData("zane" , 27 ) }fun main (args: Array <String >) { findPerson()?.apply { work() println(name) println(age) } }val br = BufferedReader(FileReader("hello.txt" )) with(br){ var line:String? while (true ) { line = readLine()?:break println(line) } close() }val br = BufferedReader(FileReader("hello.txt" ))var line:String?while (true ) { line = br.readLine()?:break println(line) } 版本一:val br = BufferedReader(FileReader("hello.txt" )).readText() 版本二:val br = BufferedReader(FileReader("hello.txt" )).readLines() 版本三: BufferedReader(FileReader("hello.txt" )).use { var line:String? while (true ){ line = it.readLine()?:break println(line) } }
四十五、尾递归优化
尾递归:函数在调用自身之后,无其它操作
tailrec 关键字提示编译器尾递归优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 data class ListNode (val value:Int , var next:ListNode? = null )tailrec fun findListNode (headNode:ListNode ?, value: Int ) : ListNode? { headNode?: return null if (headNode.value == value) return headNode return findListNode(headNode.next,value) }fun main (args: Array <String >) { val MAX_NODE_COUNT = 100000 val headNode = ListNode(0 ) var p = headNode for (i in 1. .MAX_NODE_COUNT) { p.next = ListNode(i) p = p.next!! } println(findListNode(headNode, MAX_NODE_COUNT -2 )?.value) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fun factorial (n:Long ) :Long { return n * factorial(n-1 ) }data class TreeNode (val value:Int ) { val left:TreeNode? = null val right:TreeNode? = null }fun findTreeNode (root:TreeNode ?, value:Int ) :TreeNode? { root?:return null if (root.value == value) return root return findTreeNode(root.left,value)?: return findTreeNode(root.right,value) }
四十六、闭包
函数运行的环境
持有函数运行状态
函数内部可以定义函数
函数内部也可以定义类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 fun makeFun () :() -> Unit { var count = 0 return fun () { println(++count) } }fun main (args: Array <String >) { val x = makeFun() x() x() x() x() }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 fun fibonacci () :() -> Long { var first = 0L var second = 1L return fun () :Long { val result = second second += first first = second - first return result } }fun main (args: Array <String >) { val x = fibonacci() println(x()) println(x()) println(x()) println(x()) println(x()) println(x()) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 fun fibonacci () : Iterable<Long > { var first = 0L var second = 1L return Iterable { object : LongIterator() { override fun hasNext () = true override fun nextLong () : Long { val result = second second += first first = second - first return result } } } }fun main (args: Array <String >) { for (i in fibonacci()) { if (i > 100 ) break println(i) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 fun add (x:Int ) = fun (y:Int ) = x + yfun add (x:Int ) :(Int )->Int { return fun (y:Int ) :Int { return x + y } }fun main (args: Array <String >) { val sum = add(6 ) println(sum(2 )) }
四十七、函数复合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 val add = fun (i:Int ) :Int { return i + 5 }val add = {i:Int -> i + 5 } val multiply = fun (i:Int ) :Int { return i * 2 }val multiply = { i:Int -> i * 2 } fun main (args: Array <String >) { println(multiply(add(9 ))) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 val add = {i:Int -> i + 5 } val multiply = { i:Int -> i * 2 } fun main (args: Array <String >) { val addMultiply = add andThen multiply println(addMultiply(9 )) val addComposeMultiply = add compose multiply println(addComposeMultiply(9 )) }infix fun <P1,P2, R> Function1<P1, P2> .andThen (function: Function1 <P2 , R>) :Function1<P1, R> { return fun (p1: P1 ) :R { return function.invoke(this .invoke(p1)) } }infix fun <P1,P2,R> Function1<P2,R> .compose (function: Function1 <P1 ,P2>) :Function1<P1,R> { return fun (p1: P1 ) :R { return this .invoke(function.invoke(p1)) } }
四十八、科理化(Currying)-函数调用链
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 fun hello (x:String , y:Int , z:Double ) :Boolean { println("$x $y $z " ) return true }fun curriedHello (x:String ) :(y:Int ) -> (z:Double ) -> Boolean { return fun (y:Int ) :(z:Double ) -> Boolean { return fun (z:Double ) :Boolean { println("$x $y $z " ) return true } } }fun curriedHello (x:String ) :(y:Int ) -> (z:Double ) -> Boolean = fun (y:Int ) :(z:Double ) -> Boolean = fun (z:Double ) : Boolean { println("$x $y $z " ) return true }fun curriedHello (x:String ) = fun (y:Int ) = fun (z:Double ) :Boolean { println("$x $y $z " ) return true }fun main (args: Array <String >) { hello("TAG" , 0 , 2.0 ) curriedHello("TAG" )(11 )(3.0 ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 fun log (tag:String , target: OutputStream , message:Any ?) { target.write("[$tag ]:$message \n" .toByteArray()) }fun logCurried (tag:String ) :(target:OutputStream)->(message:Any?) -> Unit { return fun (target:OutputStream ) :(message:Any?) ->Unit { return fun (message:Any ?) { target.write("[$tag ]:$message \n" .toByteArray()) } } }fun logCurried (tag:String ) :(target:OutputStream) ->(message:Any?) -> Unit = fun (target:OutputStream ) :(message:Any?) -> Unit = fun (message:Any ?) { target.write("[$tag ]:$message \n" .toByteArray()) }fun logCurried (tag:String ) = fun (target:OutputStream ) = fun (message:Any ?) { target.write("[$tag ]:$message \n" .toByteArray()) }fun logCurried (tag:String ) = fun (target:OutputStream ) = fun (message:Any ?) = target.write("[$tag ]:$message \n" .toByteArray())fun main (args: Array <String >) { log("TAG" , System.out , "Hello Log" ) logCurried("TAG" )(System.out )("Hello LogCurried" ) }
1 2 3 4 5 6 7 8 9 10 11 12 fun log (tag:String , target: OutputStream , message:Any ?) { target.write("[$tag ]:$message \n" .toByteArray()) }fun <P1,P2,P3,R> Function3<P1,P2,P3,R> .curried () = fun (p1:P1 ) = fun (p2:P2 ) = fun (p3:P3 ) = this (p1,p2,p3)fun main (args: Array <String >) { ::log.curried()("TAG" )(System.out )("Hello curried" ) }
四十九、偏函数
需求:针对上面的科理化log函数的案例,我们发现了一个问题,tag和target这两个属性其实是不变的,如果每次调用的话都需要传一遍,这就不符合我们的代码风格了(简洁丫)
1 2 3 4 5 6 7 8 9 10 11 12 13 fun log (tag:String , target: OutputStream , message:Any ?) { target.write("[$tag ]:$message \n" .toByteArray()) }fun <P1,P2,P3,R> Function3<P1,P2,P3,R> .curried () = fun (p1:P1 ) = fun (p2:P2 ) = fun (p3:P3 ) = this (p1,p2,p3)fun main (args: Array <String >) { val consoleLogWithTag = ::log.curried()("TAG" )(System.out ) consoleLogWithTag("Hello 偏函数" ) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 val makeString = fun (byteArray:ByteArray , charset:Charset ) : String { return String(byteArray, charset) }val makeStringFromGbkBytes = makeString.partial2(charset("GBK" ))fun <P1,P2,R> Function2<P1,P2,R> .partial2 (p2:P2 ) = fun (p1:P1 ) = this (p1,p2)fun main (args: Array <String >) { val bytes = "我是中国人" .toByteArray(charset("GBK" )) val stringFromGBK = makeStringFromGbkBytes(bytes) println(stringFromGBK) }
小结:统计文件内字符串个数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 fun main (args: Array <String >) { val map = HashMap<Char ,Int >() File("build.gradle" ).readText().toCharArray().filterNot(Char ::isWhitespace).forEach { val count = map[it] if (count == null ) map[it] = 1 else map[it] = count + 1 } map.forEach(::println) }fun main (args: Array <String >) { File("build.gradle" ).readText().toCharArray().filterNot(Char ::isWhitespace).groupBy { it }.map { it.key to it.value.size }.forEach(::println) }