共计 8870 个字符,预计需要花费 23 分钟才能阅读完成。
在上篇笔记中,咱们对 Kotlin 的根本类型、关键字、类与对象,以及与 Java 之间互调的内容有了一些意识,这篇笔记来看看 Kotlin 中几种非凡的类,以及汇合相干的罕用操作。
1. Kotlin 数据类
Kotlin 语言中有个非凡的类叫 数据类 ,这个类型是 Kotlin 专门用来示意一个 Java Bean 的,为啥这么说?因为它帮咱们主动生成了一个数据类应该有的办法。比方:getter/setter 办法、toString()、hashCode()、equals()、copy() 办法。举个栗子:
// code 1 | |
data class Book(val id: Int, val name: String) {} |
Kotlin 代码就这么点,而后再看看为咱们生成的 Java 代码:(如何查看生成的 Java 代码?找到 AndroidStudio 的 Tools 选项,顺次选 Kotlin、show Kotlin ByteCode,而后在新呈现的代码窗口点击 Decompile 就能够看到生成的 Java 代码了)
// code 2 | |
public final class Book { | |
private final int id; | |
@NotNull | |
private final String name; | |
public final int getId() {return this.id;} | |
@NotNull | |
public final String getName() {return this.name;} | |
public Book(int id, @NotNull String name) {Intrinsics.checkNotNullParameter(name, "name"); | |
super(); | |
this.id = id; | |
this.name = name; | |
} | |
public final int component1() {return this.id;} | |
@NotNull | |
public final String component2() {return this.name;} | |
@NotNull | |
public final Book copy(int id, @NotNull String name) {Intrinsics.checkNotNullParameter(name, "name"); | |
return new Book(id, name); | |
} | |
// $FF: synthetic method | |
public static Book copy$default(Book var0, int var1, String var2, int var3, Object var4) {if ((var3 & 1) != 0) {var1 = var0.id;} | |
if ((var3 & 2) != 0) {var2 = var0.name;} | |
return var0.copy(var1, var2); | |
} | |
@NotNull | |
public String toString() {return "Book(id=" + this.id + ", name=" + this.name + ")"; | |
} | |
public int hashCode() { | |
int var10000 = this.id * 31; | |
String var10001 = this.name; | |
return var10000 + (var10001 != null ? var10001.hashCode() : 0); | |
} | |
public boolean equals(@Nullable Object var1) {if (this != var1) {if (var1 instanceof Book) {Book var2 = (Book)var1; | |
if (this.id == var2.id && Intrinsics.areEqual(this.name, var2.name)) {return true;} | |
} | |
return false; | |
} else {return true;} | |
} | |
} |
想不到吧?一行 Kotlin 居然帮咱们干了这么多事!这也阐明用 Kotlin 的确能缩小代码量,晋升 Coding 效率。再看看 Kotlin 数据类的特点。
首先,data class 是一个 final 类,阐明 data class 是无奈被继承的,哪怕在它后面加 open 关键字也不行。仔细的同学可能发现了:这个 Book 并没有 setter 办法呀?没错,那是因为之前在写 Kotlin 代码时,把 id 和 name 两个参数设置为 val 不可变变量了,如果改成 var 再次生成一下,你就会发现有 setter 办法了。
而后,其余的 hashCode、toString、equals、copy 办法都是惯例的一些操作,当然,也能够在 data class 外面重写这些办法,达到本人想要的成果。
再看一眼,发现还有两个比拟生疏的办法:component1() 和 component2(),和 getter 办法一样啊,为啥?其实这两个办法是 data class 用来解构的,不便用户间接拿到某个对象中的属性值,如下:
// code 3 | |
val book = Book(1, "鲁迅文集") | |
book.id = 23 // setter 办法 | |
val(bookId, bookName) = book // 对 book 间接解构 | |
print("bookName = $bookName") // 输入:“bookName = 鲁迅文集” |
如果还有第三个属性的话一样的,就会给你生成 component3(),以此类推。这两个 component 是 data class 主动生成的,如果一般的 class 也想这样解构,也能够,须要应用 operator 关键字,对类中的属性做一个关联:
// code 4 | |
class Car(var brand: String, var price: Double) { // 一般类 解构 | |
operator fun component1(): String {return this.brand} | |
operator fun component2(): Double {return this.price} | |
} |
operator 关键字能够用来重载操作符或者实现一个约定。这里就是实现了一个约定。这样写之后就能够像 data class 一样进行解构了。Kotlin 的这种解构形式用的比拟多的中央是在 Map 数据结构中,它不须要像 Java 一样先拿到 Entry 对象,而后能力拿到 key 值和 value 值,间接用这种解构即可。
// code 5 | |
val person = mapOf<String, String>("name" to "Jack", "age" to "23") | |
for ((key, value) in person) {println("key: $key ==== value: $value") | |
} // (是不是比 Java 不便多了?) |
2. Kotlin 枚举类(密闭类)
Kotlin 中也有枚举类,应用办法与 Java 的枚举类一样,此外,Kotlin 还有一个更加弱小的枚举类,称为密闭类,用 sealed 关键字润饰。密闭类能够有本人的子类,而且能够扩大它的子类,例如给子类设置一些参数等,要晓得,枚举类是不具备这种特点的:
// code 6 | |
// Kotlin 密闭类(高级枚举类)sealed class Language {object English: Language() | |
object Japanese: Language() | |
class Chinese(var isDialect: Boolean): Language() // 是否方言} |
3. Kotlin 循环写法
这个就没啥可说了,间接上代码!
// code 7 | |
for (i in 1..10) { // 留神 x..y 示意的是 x<= i <= y,都是闭区间,且 x <= y,否则 i 为空 | |
print("$i") // 输入 1 2 3 4 5 6 7 8 9 10 | |
} | |
for (i in 1 until 10) { // 区间为 前闭后开 | |
print("$i") // 输入 1 2 3 4 5 6 7 8 9 | |
} | |
for (i in 10 downTo 1) { // 递加循环 | |
print("$i") // 输入 10 9 8 7 6 5 4 3 2 1 | |
} | |
for (i in 1..10 step 2) { // step 为步进长 | |
print("$i") // 输入 1 3 5 7 9 | |
} | |
repeat(10) { // 高阶函数,大括号里示意的是 Lambda 表达式,传进的参数为循环执行这个表达式多少次,it 示意的是目前执行的是第几次,从 0 开始 | |
print(it) // 输入 0123456789 | |
} | |
// 平时用的比拟多的还是取汇合中的元素 | |
val list = listOf("I","am","Chinese") | |
for (str in list) {print("$str") | |
} | |
for ((index, str) in list.withIndex()){print("第 ${index} 元素是 $str") | |
} |
4. Kotlin 罕用汇合
在 Kotlin 中罕用的汇合类次要有 List:有序汇合,可通过索引拜访元素;Set:惟一元素汇合,无反复元素的汇合;Map:键值对汇合,键是惟一的。这 3 种都是 汇合接口 ,这些都和 Java 中的一样。
Kotlin 的汇合也能够依据是否可变分为两大类:可变汇合 和 不可变汇合。不可变汇合就是汇合中的元素是不能够被批改的,没有 add、remove 等写操作的办法。个别在申明的时候就定义了汇合是否可变,可变的汇合申明就是 mutableListOf()、mutableSetOf()、mutableMapOf(). 不可变的汇合申明是 listOf()、setOf()、mapOf(). 当然,如果在申明的时候就晓得汇合里的元素,就能够应用这些函数进行初始化:
// code 8 | |
val list = listOf("a", "b", "c", "d") // 不可变汇合申明 | |
val mutableMap = mutableMapOf("name" to "Tom", "age" to "99") // 可变汇合申明 |
创立空集合的函数用的比拟少,别离是 emptyList()、emptySet()、emptyMap()。而且创立进去的都是只读类型的汇合,又不能往里面放元素,所以用的少。
用的较多的当然还是构造函数,以 List 作为阐明,Set 和 Map 以此类推。
// code 9 | |
val doubled = List(3, { it * 2}) // 通过索引 index 初始化汇合,初始化了元素个数为 3 的不可变链表。如果须要可变,则用 MutableList | |
println(doubled) // 输入:[0, 2, 4] | |
// 创立具体类型的汇合,例如 ArrayList 或 LinkedList,其实这些最初都是调用的 Java 中相应汇合类 | |
val linkedList = LinkedList<String>(listOf("one", "two", "three")) // 创立链表 |
Kotlin 当然也有数组汇合 Array,然而用的并不是很多,基本上以上 3 种汇合都能够胜任了。Array 的申明:
// code 10 | |
val array = Array(5, { it + 1}) // 初始化长度为 5 的数组 [1,2,3,4,5] | |
array[0] = 99 // array 可批改元素值,批改后为 [99,2,3,4,5] |
与 code 9 中的 List 不一样的是,Array 的这种初始化后的 array 对象,是能够对数组中的元素做批改的,然而其余的 List、Set、Map 都不行。
5. Kotlin 汇合操作符
Kotlin 为汇合扩大了许多操作符,而且这些操作符还反对链式调用,十分不便。相比于 RxJava 来说,代码量会少很多。照例还是举个栗子吧。上面的例子是将一段字符进行加密转换的操作,先看看 RxJava 是怎么解决的。
// code 8 | |
public class RxJavaExample {public static void main(String[] args) {final String[] a = new String[]{"4","0","7","i","f","w","0","9"}; | |
final Integer[] index = new Integer[]{5,3,9,4,8,3,1,9,2,1,7}; | |
Observable.just(index) // 1. 传入 index 数组 | |
.flatMap(new Function<Integer[], ObservableSource<Integer>>() { | |
// flatMap 可将汇合拆成一个个元素返回 | |
@Override | |
public ObservableSource<Integer> apply(Integer[] integers) throws Throwable { | |
// 2. 调用 flatMap 办法将数组中的 Integer 元素一个个取出 | |
return Observable.fromArray(integers); | |
} | |
}) | |
.filter(new Predicate<Integer>() { | |
@Override | |
public boolean test(Integer i) throws Throwable { | |
// 3. 调用 filter 办法,将小于 a 数组长度的 元素 过滤出来 | |
// 失去 5,3,4,3,1,2,1,7 | |
return i < a.length; | |
} | |
}) | |
.map(new Function<Integer, String>() { | |
// map 可将传入的参数类型,进行转换后输入,这里就是 Integer 转为 String 类型 | |
@Override | |
public String apply(Integer integer) throws Throwable {// 4. 调用 map 办法,将失去的一串数字再顺次作为 integer 参数输出 a[integer] 中 | |
// 失去 "w","i","f","i","0","7","0","9" | |
return a[integer]; | |
} | |
}) | |
.reduce(new BiFunction<String, String, String>() { | |
@Override | |
public String apply(String s, String s2) throws Throwable { | |
// 5. 调用 reduce 办法,将 失去的字符串两两组合 | |
// 失去 "wifi0709" | |
System.out.println("两头后果:s =" + s + "s2 =" + s2); | |
return s + s2; | |
} | |
}) | |
.subscribe(new Consumer<String>() { | |
@Override | |
public void accept(String s) throws Throwable {System.out.println("最终后果:" + s); | |
} | |
}); | |
} | |
} |
RxJava 中有许多的操作符,这里只列举了几个,它们的用法在正文里都能够找到。其中 reduce 的操作我之前是有点不分明,找到的答案都是说,传入的第一个变量和第二个变量通过解决后,顺次两两组合解决,最终失去后果。但到底是怎么个“两两组合解决”?在这里我打印出了两头后果就高深莫测了:
再来看看 Kotlin 的写法:
// code 9 | |
object CollectionOperator {private val a = arrayOf("4", "0", "7", "i", "f", "w", "0", "9") | |
private val index = arrayOf(5, 3, 9, 4, 8, 3, 1, 9, 2, 1, 7) | |
fun exampleOutput() { | |
index | |
.filter {it < a.size} | |
.map {a[it] | |
} | |
.reduce { s, s2 -> | |
"$s$s2" | |
} | |
.also {println("最终后果:s = $it") | |
} | |
} | |
} |
没想到,Kotlin 的好多汇合操作符跟 RxJava 命名都是一样的!而且更好用,比方 Kotlin 不必 flatMap,主动就将汇合中的元素拆成一个个的了,filter 外面的 it 就是代表的汇合中的一个个的元素。map、reduce 这不就是借鉴的 RxJava 吗?而且 Kotlin 的 Lambda 表达式使得代码更为简洁。
除了上述的汇合操作符,常见的还包含上面的几个操作符,间接在代码中解说更分明:
// code 10 | |
val test = listOf("2","3","x","2","g","9","k","o") // 测试汇合 | |
// 1、contains:判断是否含有某元素 | |
println(test.contains("x")) // 输入:true | |
println(test.contains("w")) // 输入:false | |
// 2、elementAt:返回对应的索引元素,越界会抛出 IndexOutOfBoundsException | |
println(test.elementAt(3)) // 输入:"2" | |
// 3、firstOrNull:找出满足条件的第一个元素,若无则返回 null | |
println(test.firstOrNull { it == "3"}) // 输入:"3" | |
// 4、indexOf:返回指定元素的索引,若无则返回 -1 | |
println(test.indexOf("k")) // 输入:6 | |
// 5、singleOrNull:返回满足条件的单个元素,若没有元素满足条件或不止一个元素满足,则返回 null | |
test.singleOrNull{it == "2"}.also {println(it) // 输入:null | |
} | |
// 6、any:汇合中任意一个元素若满足条件,则返回 true;否则返回 false | |
println(test.any { it == "o"}) // 输入:true | |
val list = listOf(23,-1,4,6,888,34) | |
// 7、all:汇合中所有元素都满足条件,则返回 true;否则返回 false | |
println(list.all { it < 1000}) // 输入:true | |
// 8、none:汇合中所有元素都不满足条件,则返回 true;否则返回 false | |
println(list.none { it - 100 > 100}) // 输入:false | |
// 9、count:返回汇合中满足条件的元素个数 | |
println(list.count { it > 0}) // 输入:5 | |
// 10、reduce:汇合中第一项和第二项进行解决,得出的后果再和第三项进行解决,始终解决到最初一个元素 | |
println(list.reduce { v1, v2 -> | |
v1 + v2 | |
}) // 输入:954 | |
// 11、filter 操作符,过滤,Lambda 表达式是过滤的条件 | |
println(list.filter { it > 0}) // 输入:[23, 4, 6, 888, 34] | |
// 12、filterNot 与 filter 是互补的,输入 filter 过滤掉的 | |
println(list.filterNot { it > 0}) // 输入:[-1] | |
// 13、filterNotNull:过滤掉 null 的元素 | |
println(listOf(null, 3, 5, null, 8).filterNotNull()) | |
// 14、take:取前 n 个元素 | |
println(list.take(3)) // 输入:[23, -1, 4] | |
// 15、mapIndexed:带 index 索引信息的 map | |
println(listOf(35,61,116,74).mapIndexed{ index, i -> | |
(i+index).toChar()}) // 输入:[#, >, v, M] | |
// 16、mapNotNull:执行 map 转换前须要过滤掉 null 的元素 | |
println(listOf(null, 33, 55, null, 88).mapNotNull {it?.toChar() | |
}) // 输入:[!, 7, X] | |
// 17、groupBy:分组操作符。这里就是将 list 分为偶数奇数两组,返回一个 Map | |
println(list.groupBy { it % 2 == 0}) // 输入:{false=[23, -1], true=[4, 6, 888, 34]} | |
// 18、reversed:生成一个与输出汇合反序的汇合 | |
println(list.reversed()) // 输入:[34, 888, 6, 4, -1, 23] | |
// 19、sorted:生成一个升序排列汇合,原汇合不变 | |
println(list.sorted()) | |
// 20、sortedDescending:生成一个降序排列汇合,原汇合不变 | |
println(list.sortedDescending()) | |
// 21、sortedBy:自定义排序,次要针对 Pair 进行排序 | |
val pairList = listOf(1 to "A", 2 to "B", 5 to "C", 3 to "D") | |
println(pairList.sortedBy {it.first}) // 输入:[(1, A), (2, B), (3, D), (5, C)] | |
println(pairList.sortedBy {it.second}) // 输入:[(1, A), (2, B), (5, C), (3, D)] |
与 RxJava 相似,Kotlin 也有一个 flatMap 操作符,有许多同学不禁会问了:map 和 flatMap 有啥区别啊?能够依据名称来记,flatMap 有 flat,flat 英文意思是:平的,扁平的;使扁平,它有把汇合数据拆开,铺展开的操作。再加个实例阐明:
// code 11 | |
val multipList = listOf(listOf(1,9,4,6), listOf("R","U","OK")) | |
println(multipList.map { it}) // 输入:[[1, 9, 4, 6], [R, U, OK]] | |
println(multipList.flatMap { it}) // 输入:[1, 9, 4, 6, R, U, OK] |
所以还是那句话: 纸上得来终觉浅,绝知此事要躬行 。
更多 Android 学习笔记能够查看我的集体公众号: 修之竹
参考文献
- 张涛; 极客工夫 Kotlin 系列课程
- Kotlin 官网中文文档。https://www.kotlincn.net/docs/reference/collections-overview….
- 全汪汪; Kotlin-Map 和 flatMap。https://www.jianshu.com/p/696d307c2c59
- 东海陈光剑; Kotlin 中的汇合类排序。https://blog.csdn.net/universsky2015/article/details/83593126
ps. 赠人玫瑰,手留余香。欢送转发分享,你的认可是我持续创作的精力源泉。