2011年7月,JetBrains推出Kotlin项目,这是一个面向JVM的新语言,它已被开发数年之久,它是一种在Java虚拟机上运行的静态类型编程语言,也可以被编译成为JavaScript源代码。它主要是由俄罗斯圣彼得堡的JetBrains开发团队所发展出来的编程语言,其名称来自于圣彼得堡附近的科特林岛。2012年1月,著名期刊《Dr. Dobb's Journal》中它被认定为该月的最佳语言。虽然与Java语法并不兼容,但Kotlin被设计成可以和Java代码相互运作,并可以重复使用如Java集合框架等的现有Java引用的函数库。
容器类型 Kotlin号称全面兼容Java,于是乎Java的容器类仍可在Kotlin中正常使用,包括大家熟悉的队列ArrayList、映射HashMap等等。不过Kotlin作为一门全新的语言,肯定还是要有自己的容器类,不然哪天Java跟Kotlin划清界限,那麻烦就大了。与Java类似,Kotlin也拥有三类基本的容器,分别是集合Set、队列List、映射Map,然后每类容器又分作只读与可变两种类型,这是为了判断该容器能否进行增删改等变更操作。Kotlin对修改操作很慎重,比如变量用val前缀表示不可修改,用var前缀表示允许修改;类默认是不允许继承的,只有添加open前缀才允许该类被继承;至于容器默认为只读容器,如果需要进行修改则需加上Mutable形成新的容器,比如MutableSet表示可变集合,MutableList表示可变队列,MutableMap表示可变映射。 既然Set/List/Map都属于容器,那么必定拥有相同的基本容器方法,具体说明如下:
1 2 3 4 5 6 isEmpty() : 判断该容器是否为空。 isNotEmpty() : 判断该容器是否非空。 clear() : 清空该容器。 contains() : 判断该容器是否包含指定元素。 iterator() : 获取该容器的迭代器。 count() : 获取该容器包含的元素个数,也可通过size属性获得元素数量。
初始化赋值 : Kotlin允许在声明容器变量之时进行初始赋值,这点很方便比Java先进,当然不同容器的初始化方法有所区别,具体的对应关系见下表:
1 2 3 4 5 6 7 名称: 初始化方法: 只读集合 Set setOf() 可变集合 MutableSet mutableSetOf() 只读队列 List listOf() 可变队列 MutableList mutableListOf() 只读映射 Map mapOf() 可变映射 MutableMap mutableMapOf()
以上是Kotlin容器的基本方法,更具体的增删改查等用法则有所不同,下面分别介绍这三类六种容器的详细用法。
List类型 List是一种元素之间按照顺序排列的容器,它与集合的最大区别,便是多了个次序管理。正因为List建立了秩序规则,所以它比Set多提供了如下功能(注意凡是涉及到增删改的,都必须由MutableList来完成):
1 2 3 4 List<Int >?:List 本身可空,List 的元素不可空 List<Int ?> :List 本身不可空,List 的元素可空 List<Int ?>? :List 本身可空,List 的元素也可空
对于List的遍历操作:for循环,迭代器循环,forEach循环,以及下标遍历。用法如下:
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 fun main (args: Array <String >) {val mylist:MutableList<String> = mutableListOf("联想" ,"联想 Y520" ,"惠普" ,"戴尔" ,"IBM" ,"长城" ,"方正" ,"联想 Y720" ,"联想 Y7000" );print("电脑畅销榜已添加,当前共有${mylist.size} 款电脑\n" ) print("for-in 电脑畅销榜包含以下${mylist.size} 款电脑\n" ) for (item in mylist){print("名称:${item} \n" ) } if (mylist.size>=2 ){mylist.removeAt(1 ); } print("removeAt电脑畅销榜已更新,当前包含以下${mylist.size} 款电脑\n" ) print("for-in 电脑畅销榜包含以下${mylist.size} 款电脑\n" ) for (item in mylist){print("名称:${item} \n" ) } mylist.sortBy { it.first() } print("for-in sort电脑畅销榜已按照it升序重新排列,包含以下${mylist.size} 款电脑\n" ) for (item in mylist){print("名称:${item} \n" ) } mylist.sortByDescending { it.first() } print("for-in sort电脑畅销榜已按照it降序重新排列,包含以下${mylist.size} 款电脑\n" ) for (item in mylist){print("名称:${item} \n" ) } print("iterator-while 电脑畅销榜包含以下${mylist.size} 款电脑\n" ) var iter = mylist.iterator()while (iter.hasNext()){print("名称:${iter.next()} \n" ) } print("foreach 电脑畅销榜包含以下${mylist.size} 款电脑\n" ) mylist.forEach { print("名称:${it} \n" ) } print("indices电脑畅销榜包含以下${mylist.size} 款电脑\n" ) for (i in mylist.indices){print("名称:${mylist[i]} \n" ) } }
Set类型 Set集合是一种简单的容器,它具有以下特性:
对于Set的遍历操作:for循环,迭代器循环,forEach循环,三种循环遍历的用法如下:
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 private val goodsA:String="惠普" private val goodsB:String="联想" private val goodsC:String="戴尔" private val goodsD:String="IBM" private val goodsE:String="长城" private val goodsF:String="方正" private val myset:MutableSet<String> = mutableSetOf();fun main (args: Array <String >) {myset.add(goodsA) myset.add(goodsB) myset.add(goodsC) myset.add(goodsD) myset.add(goodsE) myset.add(goodsF) myset.remove(goodsA) print("电脑畅销榜已添加,并且remove goodsA商品惠普,当前共有${myset.size} 款电脑\n" ) print("for-in 电脑畅销榜包含以下${myset.size} 款电脑\n" ) print("for-in测试\n" ) for (item in myset){print(item+"\n" ) } print("iterator-while 电脑畅销榜包含以下${myset.size} 款电脑\n" ) print("iterator测试\n" ) var iter = myset.iterator()while (iter.hasNext()){print(iter.next()+"\n" ) } print("foreach 电脑畅销榜包含以下${myset.size} 款电脑\n" ) print("foreach测试\n" ) myset.forEach { print(it+"\n" ) } print("foreach\$拼接表达式电脑畅销榜包含以下${myset.size} 款电脑\n" ) var dec="" myset.forEach { dec="${dec} 名称:${it} \n" } print("$dec " ) }
Map类型 映射内部保存的是一组键值对(Key-Value),也就是说,每个元素都由两部分构成,第一部分是元素的键,相当于元素的名字;第二部分是元素的值,存放着元素的详细信息。元素的键与值是一一对应的关系,相同的键名指向的值对象是唯一的,所以映射中每个元素的键名各不相同,这个特性使得映射的变更操作与队列存在以下不同之处(注意增删操作必须由MutableMap来完成):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var goodsMap = mapOf("苹果" to goodsA, "华为" to goodsB, "小米" to goodsC, "欧珀" to goodsD, "步步高" to goodsE, "魅族" to goodsF)var goodsMutMap = mutableMapOf(Pair("苹果" , goodsA), Pair("华为" , goodsB), Pair("小米" , goodsC), Pair("欧珀" , goodsD), Pair("步步高" , goodsE), Pair("魅族" , goodsF))遍历方式与Set相似,如下 fun main (args: Array <String >) {var goodsMutMap=mutableMapOf<String, String>(Pair("苹果" , goodsA),Pair("华为" , goodsB),Pair("戴尔" , goodsC),Pair("华硕" , goodsD),Pair("弘基" ,goodsE),Pair("联想" , goodsE));for (item in goodsMutMap){print("厂家:${item.key} ,名称:${item.value} \n" ); } var iterator= goodsMutMap.iterator();while (iterator.hasNext()){var item=iterator.next();print("厂家:${item.key} 名称:${item.value} \n" ) } goodsMutMap.forEach{ print("厂家:${it.key} ,名称:${it.value} \n" ) } }
泛型函数 泛型的引入:函数的输入参数类型必须在定义函数时就要指定,可是有时候参数类型是不确定的,只有在函数调用时方能知晓具体类型,如此一来要怎样声明函数呢? 定义泛型函数时,得在函数名称前面添加“”,表示以T声明的参数(包括输入参数和输出参数),其参数类型必须在函数调用时指定。 实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 fun <T> appendString (tag:String , var otherInfo:T ?) :String{var str:String = "$tag :" for (item in otherInfo){str="$str ${item.toString()} " } return str} fun main (args: Array <String >) {var count = 0 when (count%3 ){0 -> appendString<String>("古代的四大发明" ,"造纸术" ,"印刷术" ,"火药" ,"指南针" )1 -> appendString<Int >("小于10的素数" ,2 ,3 ,5 ,7 )else -> appendString<Double >("烧钱的日子" ,5.20 ,6.18 ,11.11 ,12.12 )} }
内联函数 注意到前面定义泛型函数appendString,是把它作为一个全局函数,也就是在类外面定义,不在类内部定义。因为类的成员函数依赖于类,只有泛型类(又称模板类)才能拥有成员泛型函数,普通类是不允许定义泛型函数的,否则编译器会直接报错。不过有个例外情况,如果参数类型都是继承自某种类型,那么允许在定义函数时指定从这个基类泛化开,凡是继承自该基类的子类,都可以作为输入参数进行函数调用,反之则无法调用函数。 举个例子,Int、Float和Double都继承自Number,但是定义一个setArrayNumber(array:Array)函数,它并不接受Array或者Array的入参,如果要让该方法同时接受源自Number的数组入参,就得定义泛化自Number的泛型函数,即将改为,同时在fun前面添加关键字inline,表示该函数也为内联函数。内联函数在编译之时,会在调用处把该函数的内部代码直接复制一份,调用十次就会复制十份,而非普通函数那样仅仅提供一个函数的访问地址。该例子的函数定义代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fun setArrayNumber (array:Array <Number >) {var str:String = "数组元素依次排列:" for (item in array) {str = str + item.toString() + ", " } tv_function_result.text = str } inline fun <reified T : Number> setArrayStr (array:Array <T >) {var str:String = "数组元素依次排列:" for (item in array) {str = str + item.toString() + ", " } tv_function_result.text = str }
扩展函数 系统自带的类已经提供了许多方法,然而经常还是无法完全满足业务需求,此时开发者往往要写个工具类,比如StringUtil、DateUtil之类,来补充相关的处理功能,长此以往,工具类越来越多也越来越难以管理。 基于以上情况,Kotlin推出了扩展函数的概念,允许开发者给系统类补写新的方法,而无需另外编写额外的工具类。比如系统自带的数组Array提供了求最大值的max方法,提供了进行排序的sort方法,可是并未提供交换数组元素的方法。于是我们打算给Array增加新的交换方法,也就是添加一个扩展函数swap,与众不同的是要在函数名称前面加上“Array.”,表示该函数扩展自Array。swap函数的定义代码如下所示:
1 2 3 4 5 fun Array<Int> .swap (pos1:Int , pos2:Int ) {val temp = this [pos1]this [pos1] = this [pos2]this [pos2] = temp}
与泛型函数结合可以增强它的泛用性
1 2 3 4 5 fun <T> Array<T> .swap (pos1:Int , pos2:Int ) {val temp = this [pos1]this [pos1] = this [pos2]this [pos2] = temp}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 fun main (args: Array <String >) {var city :MutableList<String> = mutableListOf( "Shanghai Job Offered" , "Beijing Events" , "Beijing Language" ,"Beijing Massage & Escort" ,"Beijing Language" ,"Beijing Events" ,"Shanghai Job Offered" )var testNum :MutableList<Int > = mutableListOf( 1 ,2 ,3 ,4 ,1 ,2 ,3 ,4 ,5 )testNum.sort() for (item in testNum.deleteDuplication()){println("$item " ) } city.sort() for (item in city.deleteDuplication()){println("$item " ) } } fun <T> MutableList<T> .deleteDuplication () :MutableList<T>{var res:MutableList<T> = mutableListOf()for (i in this .indices){if (i>0 && this [i]==this [i-1 ]) continue else res.add(this [i])} return res}
尾递归函数 Kotlin引入了扩展函数,还能反过来精简函数。具体地说,如果一个函数的表达式比较简单,一两行就可以搞定的话,Kotlin允许使用等号代替大括号。 例如:5!=54 32 1
1 2 3 4 fun factorial (n:Int ) :Int {if (n <= 1 ) nelse n*factorial(n-1 )}
可以简化成:
1 fun factorial (n:Int ) :Int = if (n <= 1 ) n else n*factorial(n-1 )
Kotlin体系还存在一种特殊的递归函数,名叫尾递归函数,它指的是函数末尾的返回值重复调用了自身函数。此时要在fun前面加上关键字tailrec,告诉编译器这是个尾递归函数,则编译器会相应进行优化,从而提高程序性能。
1 2 3 4 5 6 tailrec fun findFixPoint (x: Double = 1.0 ) : Double = if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
高阶函数 前面多次提到函数被Kotlin当作特殊变量,包括函数声明采取跟变量声明一样的形式“名称:类型”,以及简化函数允许直接用等号连接函数体等等,那么本节最后讲述的则是把A函数作为B函数的输入参数,就像普通变量一样参与B函数的表达式计算。此时因为B函数的入参内嵌了A函数,故而B函数被称作高阶函数,对应的A函数则为低阶函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 fun <T> maxCustom (array: Array <T >, greater: (T , T ) -> Boolean ) : T? {var max: T? = null for (item in array)if (max == null || greater(item, max))max = item return max} fun main (args: Array <String >) {var string_array:Array<String> = arrayOf("How" , "do" , "you" , "do" , "I'm " , "Fine" )println("字符串数组的默认最大值为${string_array.max()} " ) println("字符串数组按长度比较的最大值为${maxCustom<String>(string_array, { a, b -> a.length > b.length } )}" ) println("字符串数组的默认最大值(使用高阶函数)为${maxCustom(string_array, { a, b -> a > b } )}" ) println("字符串数组按去掉空格再比较长度的最大值为${maxCustom(string_array, { a, b -> a.trim().length > b.trim().length } )}" ) }
前述的高阶函数maxCustom同时结合了泛型函数的写法,其实还可以给它加上扩展函数的功能。
1 2 3 4 5 6 7 fun <T> Array<T> .maxCustom (array: Array <T >, greater: (T , T ) -> Boolean ) : T? {var max: T? = null for (item in array)if (max == null || greater(item, max))max = item return max}
最后,感谢好朋友的分享:) Kotlin学习笔记 By Soul丶Knight