kmm跨平台
Kotlin 多平台 · Kotlin 官方文档 中文版
kotlin构造器
https://rengwuxian.com/kotlin-basic-3/
https://juejin.cn/post/6844903872016678919
我们项目中还有根据判断条件设置View显隐的逻辑,针对这个再封装下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| fun View.hide() { this.visibility = View.GONE }
fun View.show() { this.visibility = View.VISIBLE }
fun View.showIf(isShow: Boolean) { if (isShow) { show() } else { hide() } }
|
Android仅支持部分Java8新特性
链接
kotlin
new关键字可以省略,变量类型、继承用:
val常亮,var变量
可以直接在参数里设置默认值,参数可以不传
扩展函数(类似于工具类)
forEach:遍历每一个元素
filter:对每个元素进行过滤操作,如果 lambda 表达式中的条件成立则留下该元素,否则剔除,最终生成新的集合
map:遍历每个元素并执行给定表达式,最终形成新的集合
flatMap:遍历每个元素,并为每个元素创建新的集合,最后合并到一个集合中
和rxjava的类似
创建数组,增加很多有用的工具函数
contains()first()find()
1
| val strs: Array<String> = arrayOf("a", "b", "c")
|
协变 (covariance) 特性,就是子类数组对象不能赋值给父类的数组变量
1
| val strs: Array<String> = arrayOf("a", "b", "c") val anys: Array<Any> = strs // compile-error: Type mismatch
|
map也可以这样创建
1
| val map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 3)
|
istOf() 创建不可变的 List,mutableListOf() 创建可变的 List
Kotlin 中集合分为两种类型:只读的和可变的。这里的只读有两层意思:
集合的 size 不可变
集合中的元素值不可变
可以转换
构造器
1 2 3 4
| class User constructor(var name: String)
constructor(name: String, id: Int, age: Int) : this(name, id) }
|
== :可以对基本数据类型以及 String 等类型进行内容比较,相当于 Java 中的 equals
=== :对引用的内存地址进行比较,相当于 Java 中的 ==
如果每个类型都去实现诸如 TextViewList、ActivityList 这样的具体的类型,显然是不可能的。因此就诞生了「泛型」,它的意思是把具体的类型泛化,编码的时候用符号来指代类型,在使用的时候,再确定它的类型
使用关键字 out 来支持协变,等同于 Java 中的上界通配符 ? extends。
使用关键字 in 来支持逆变,等同于 Java 中的下界通配符 ? super。
1 2
| var textViews: List<out TextView> var textViews: List<in TextView>
|
协程就是kotlin官方提供的线程api
属性委托
有些常见的属性操作,我们可以通过委托方式,让它实现,例如:lazy 延迟属性: 值只在第一次访问的时候计算
类委托
可以通过类委托来减少 extend类委托的时,编译器回优使用自身重新函数,而不是委托对象的函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| interface Base{ fun print() }
case BaseImpl(var x: Int):Base{
override fun print(){ print(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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class SeniorManager(val driver: Driver, val writer: Writer) : Driver by driver, Writer by writer class CarDriver : Driver { override fun drive() { println("开车呢") } } class PPTWriter : Writer { override fun write() { println("做PPT呢") } } interface Driver { fun drive() } interface Writer { fun write() } val driver = CarDriver() val writer = PPTWriter() val seniorManager = SeniorManager(driver, writer) seniorManager.drive() seniorManager.write()
interface Base { fun print() } class BaseImpl(val x: Int) : Base { override fun print() { print(x) } } class Derived(b: Base) : Base by b fun main(args: Array<String>) { val b = BaseImpl(10) Derived(b).print() }
|
使⽤用 类名::class 获取的是 Kotlin 的类型是 KClass
使⽤用 类名::class.java 获取的是 Java 的类型
Any
Kotlin 的顶层⽗父类是 Any ,对应 Java 当中的 Object ,但是⽐比 Object 少了了 wait()/notify()
等函数
Unit
Kotlin 中的 Unit 对应 Java 中的 void
在 Java 中通过 「类名.this」 获取⽬目标类引⽤用
在 Kotlin 中通过「this@类名」获取⽬目标类引⽤用
以前
1 2 3 4 5 6 7 8 9 10 11
| fun toast(message: String, length: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, message, length).show() }
toast("hello")
fun Context.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, message, duration).show() }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| fun whenTest(obj: Any) { when (obj) { 1 -> print("第一项") "hello" -> print("这个是字符串hello") is Long -> print("这是一个Long类型数据") !is String -> print("这不是String类型的数据") else -> print("else类似于Java中的default") } }
fun getIntValue(value: Int) = value fun add_two(x: Int, y: Int): Int = x + y
|
!! 强⾏调用符
?. 安全调用符
kotlin默认不能空,变量类型后面跟?号定义,表明这是一个可空类型
?. 代表着如果该类型为空的话就返回null不做后续的操作,如果不为空的话才会去访问对应的方法或者属性
!!. 代表着如果该类型为空的话就抛出NullPointerException,如果不为空就去访问对应的方法或者属性, 所以只有在很少的特定场景才用这种符号,代表着程序不处理这种异常的case了,会像java代码一样抛出NullPointerException。 而且代码中一定不用出现下面这种代码,会让代码可读性很差而且如果有空指针异常,我们也不能马上发现是哪空了:
/*
* 不推荐这样的写法:链式的连续用!!.
* */
val user = User()
user!!.name!!.subSequence(0,5)!!.length
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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
| val value: String? = "HelloWorld" val name: String = getName() ?: return println("$arg1 + $arg2 = ${arg1 + arg2}") val FINAL_HELLO_CHINA = "HelloChina" val args1 = arrayOf(1,2,3) val range: IntRange = 0..1024 val range_exclusive: IntRange = 0 until 1024 class A { var b = 0 lateinit var c: String lateinit var d: X val e: X by lazy { println("init X") X() } private val linearLayoutManager by lazy { LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false) } var cc: String? = null } abstract class Person(open val age: Int) { abstract fun work() }
val max = if (a > b) a else b
data class Forecast(val date: Date, val temperature: Float, val details: String)
class Person( var name:String){
private var description: String? = null
init { name = "Zhang Tao" }
internal fun sayHello() { println("hello $name") }
fun printName(){ println(name) }
constructor(name: String, description: String) : this(name) { this.description = description }
}
class Latitude private constructor(val value: Double) { companion object { @JvmStatic fun ofDouble(double: Double): Latitude { return Latitude(double) }
fun ofLatitude(latitude: Latitude): Latitude { return Latitude(latitude.value) }
@JvmField val TAG: String = "Latitude" } }
class Manager : Driver, Writer { override fun write() {
}
override fun drive() {
} }
operator fun String.times(int: Int): String { val stringBuilder = StringBuilder() for (i in 0 until int) { stringBuilder.append(this) } return stringBuilder.toString() }
val pdfPrinter = PdfPrinter() args.forEach(pdfPrinter::println) class PdfPrinter { fun println(any: Any) { kotlin.io.println(any) } }
val list = listOf<Int>(1, 2, 3, 5, 10, 8, 2) val newList = ArrayList<Int>(); list.forEach { val newElement = it * 2 + 3 newList.add(newElement) } val newList2 = list.map { it * 2 + 3 } val newList3 = list.map { Int::toDouble } newList3.forEach(::println) newList3.map(::println) list.takeWhile { it <= 3 }.forEach(::println) list.forEach { if (it % 2 == 0) { println(it) } } list.filter { it.isEvent() }.forEach(::println) val person = findPerson(); println(person?.age) println(person?.name) findPerson()?.let { person -> person.work() println(person.age) } findPerson()?.apply { work() println(age) }
return TextView(context).apply { text = "test" setOnClickListener(onClickListener) }
BufferedReader(FileReader("hello.txt")).use { var line: String? while (true) { line = it.readLine() ?: break println(line) } }
|
with的用法:
返回值是最后一行,可以直接调用对象的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| fun alphabet(): String { val result = StringBuilder() result.append("START\n") for (letter in 'A'..'Z') { result.append(letter) } result.append("\nEND") return result.toString() }
fun alphabet4(): String { return with(StringBuilder()) { append("START\n") for (letter in 'A'..'Z') { append(letter) } append("\nEND") toString() } }
|
take是从集合中取前几个元素
takeLast是从集合中取后几个元素
sortedBy 排序
过滤list,符合过滤条件的就是过滤结果
filterNot把符合条件的过滤掉,剩下的是结果。这个操作和 filter 相反
slice,取集合中的某一部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.slice(1..3)) println(numbers.slice(0..4 step 2)) println(numbers.slice(setOf(3, 5, 0)))
[two, three, four] [one, three, five] [four, six, one]
val numbers = listOf("one", "two", "three", "four", "five", "six") println(numbers.takeWhile { !it.startsWith('f') }) println(numbers.takeLastWhile { it != "three" }) println(numbers.dropWhile { it.length == 3 }) println(numbers.dropLastWhile { it.contains('i') })
[one, two, three] [four, five, six] [three, four, five, six] [one, two, three, four]
|
JAVA Lambda
闭包就是Lambda 表达式
lambda 表达式,通常是在需要一个函数,但是又不想去命名一个函数的场合下使用,也就是匿名函数。而Lambda可以简化匿名函数,使代码简洁。
也可以将一个代码块赋值给一个变量,在调用方法的时候,可以直接将这个lambda表达式当作参数传递进去链接
什么是Lambda?我们知道,对于一个Java变量,我们可以赋给其一个“值”。
如果你想把“一块代码”赋给一个Java变量,应该怎么做呢?
比如,我想把右边那块代码,赋给一个叫做aBlockOfCode的Java变量:
在Java 8之前,这个是做不到的。但是Java 8问世之后,利用Lambda特性,就可以做到了。
当然,这个并不是一个很简洁的写法。所以,为了使这个赋值操作更加elegant, 我们可以移除一些没用的声明。
这样,我们就成功的非常优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。
但是这里仍然有一个问题,就是变量aBlockOfCode的类型应该是什么?
在Java 8里面,所有的Lambda的类型都是一个接口,而Lambda表达式本身,也就是”那段代码“,需要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式本身就是一个接口的实现。直接这样说可能还是有点让人困扰,我们继续看看例子。我们给上面的aBlockOfCode加上一个类型:
这种只有一个接口函数需要被实现的接口类型,我们叫它”函数式接口“。
为了避免后来的人在这个接口中增加接口函数导致其有多个接口函数需要被实现,变成”非函数接口”,我们可以在这个上面加上一个声明@FunctionalInterface, 这样别人就无法在里面添加新的接口函数了:
这样,我们就得到了一个完整的Lambda表达式声明:
Lambda 表达式有什么作用?最直观的作用就是使得代码变得异常简洁。我们可以对比一下 Lambda 表达式和传统的 Java 对同一个接口的实现:
这两种写法本质上是等价的。但是显然,Java 8中的写法更加优雅简洁。并且,由于 Lambda 可以直接赋值给一个变量,我们就可以直接把 Lambda 作为参数传给函数, 而传统的 Java 必须有明确的接口实现的定义,初始化才行:
有些情况下,这个接口实现只需要用到一次。传统的Java 7必须要求你定义一个“污染环境”的接口实现MyInterfaceImpl,而相较之下Java 8的Lambda, 就显得干净很多。
Lambda结合FunctionalInterface Lib, forEach, stream(),method reference等新特性可以使代码变的更加简洁!
直接上例子。假设 Person 的定义和 List<Person>的值都给定。
现在需要你打印出guiltyPersons List里面所有LastName以”Z”开头的人的FirstName。
原生态Lambda写法:定义两个函数式接口,定义一个静态函数,调用静态函数并给参数赋值Lambda表达式。
这个代码实际上已经比较简洁了,但是我们还可以更简洁么?当然可以。在Java 8中有一个函数式接口的包,里面定义了大量可能用到的函数式接口(java.util.function (Java Platform SE 8 ))。
所以,我们在这里压根都不需要定义 NameChecker 和 Executor 这两个函数式接口,直接用 Java 8函数式接口包里的 Predicate<\T>和 Consumer<\T>就可以了——因为他们这一对的接口定义和 NameChecker/Executor 其实是一样的。
第一步简化 - 利用函数式接口包:
静态函数里面的 for each 循环其实是非常碍眼的。这里可以利用 Iterable 自带的 forEach()来替代。forEach()本身可以接受一个 Consumer<\T> 参数。
第二步简化 - 用Iterable.forEach()取代foreach loop:
由于静态函数其实只是对List进行了一通操作,这里我们可以甩掉静态函数,直接使用stream()特性来完成。
stream()的几个方法都是接受 Predicate<\T>,Consumer<\T>等参数的(java.util.stream (Java Platform SE 8 ))。你理解了上面的内容,stream()这里就非常好理解了,并不需要多做解释。
第三步简化 - 利用stream()替代静态函数:
对比最开始的Lambda写法,这里已经非常非常简洁了。但是如果,我们的要求变一下,变成print这个人的全部信息,及p -> System.out.println(p);
那么还可以利用Method reference来继续简化。所谓Method reference, 就是用已经写好的别的Object/Class的method来代替Lambda expression。格式如下:
第四步简化 - 如果是println(p),则可以利用Method reference代替forEach中的Lambda表达式:
这基本上就是能写的最简洁的版本了。
在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰,Java 8的方法引用允许我们这样做。
方法引用是一个更加紧凑,易读的Lambda表达式,注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号”::”。
方法引用 Person:: compareByAge 在语义上与 Lambda 表达式 (a, b) -> Person.compareByAge(a, b) 是等同的,都有如下特性:真实的参数是拷贝自 Comparator<\Person>.compare 方法,即(Person, Person);表达式体调用 Person.compareByAge 方法;可以省略掉参数
Lambda 配合 Optional<\T>可以使 Java 对于 null 的处理变的异常优雅这里假设我们有一个 person object,以及一个 person object 的 Optional wrapper:
Optional<\T>如果不结合 Lambda 使用的话,并不能使原来繁琐的 null check 变的简单。
只有当 Optional<\T>结合 Lambda 一起使用的时候,才能发挥出其真正的威力!我们现在就来对比一下下面四种常见的 null 处理中,Java 8的 Lambda+Optional<
T>和传统 Java 两者之间对于 null 的处理差异。情况一 - 存在则开干
情况二 - 存在则返回,无则返回屁
情况三 - 存在则返回,无则由函数产生
情况四 - 夺命连环null检查
由上述四种情况可以清楚地看到,Optional<\T>+Lambda 可以让我们少写很多 ifElse 块。尤其是对于情况四那种夺命连环 null 检查,传统 java 的写法显得冗长难懂,而新的 Optional<
T>+Lambda 则清新脱俗,清楚简洁。
关于Java的Lambda, 还有东西需要讨论和学习。比如如何handle lambda exception,如何利用Lambda的特性来进行parallel processing等。
总之,我只是一如既往地介绍个大概,让你大概知道,哦!原来是这样子就OK了。网上关于Lambda有很多相关的教程,多看多练。假以时日,必定有所精益。