kotlin和java一样也是一门jvm语言最后的编译结果都是.class文件,并且可以通过kotlin的.class文件反编译回去java代码,并且封装了许多语法糖,其中我在项目中常用的特性有
aaa?.let{
it.bbb
}
当aaa不为空时 执行括号内的方法
@JvmOverloads注解的作用就是:在有默认参数值的方法加上@JvmOverloads注解,则Kotlin就会暴露多个重载方法。可以减少写构造方法。
例如:没有加注解,默认参数没有起到任何作用。
fun f(a: String, b: Int = 0, c: String="abc"){
...
}
那相当于在java中:
void f(String a, int b, String c){
}
如果加上注解@JvmOverloads ,默认参数起到作用
@JvmOverloads
fun f(a: String, b: Int = 0, c: String="abc"){
...
}
相当于Java中:
三个构造方法,
void f(String a)
void f(String a, int b)
void f(String a, int b, String c)
List 返回的是EmptyList,MutableList 返回的是一个ArrayList,查看EmptyList的源码就知道了,根本就没有提供 add 方法。
internal object EmptyList : List, Serializable, RandomAccess {
private const val serialVersionUID: Long = -7390468764508069838L
override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty()
override fun hashCode(): Int = 1
override fun toString(): String = "[]"
override val size: Int get() = 0
override fun isEmpty(): Boolean = true
override fun contains(element: Nothing):
Boolean = false
override fun containsAll(elements: Collection<Nothing>): Boolean = elements.isEmpty()
override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.")
override fun indexOf(element: Nothing): Int = -1
override fun lastIndexOf(element: Nothing): Int = -1
override fun iterator(): Iterator<Nothing> = EmptyIterator
override fun listIterator(): ListIterator<Nothing> = EmptyIterator
override fun listIterator(index: Int): ListIterator<Nothing> {
if (index != 0) throw IndexOutOfBoundsException("Index: $index")
return EmptyIterator
}
override fun subList(fromIndex: Int, toIndex: Int): List<Nothing> {
if (fromIndex == 0 && toIndex == 0) return this
throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex")
}
private fun readResolve(): Any = EmptyList
饿汉式:
object StateManagementHelper {
fun init() {
//do some initialization works
}
}
懒汉式:
class StateManagementHelper private constructor() {
companion object {
private var instance: StateManagementHelper? = null
@Synchronized get() {
if (field == null) field = StateManagementHelper()
return field
}
}
fun init() {
//do some initialization works
}
}
双重检测:
class StateManagementHelper private constructor() {
companion object {
val instance: StateManagementHelper by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
StateManagementHelper()
}
}
fun init() {
//do some initialization works
}
}
静态内部类:
class StateManagementHelper private constructor() {
companion object {
val INSTANCE = StateHelperHolder.holder
}
private object StateHelperHolder {
val holder = StateManagementHelper()
}
fun init() {
//do some initialization works
}
}
在kotlin中数据类通过data关键字来修饰。
系统生成方法代码验证
数据类代码如下:
data class Result(var code:Int,val msg:String)
反编译后java代码如下:
private int code;
@NotNull
private final String msg;
//...省略setter和getter方法
public Result(int code, @NotNull String msg) {
Intrinsics.checkNotNullParameter(msg, "msg");
super();
this.code = code;
this.msg = msg;
}
public final int component1() {
return this.code;
}
@NotNull
public final String component2() {
return this.msg;
}
@NotNull
public final Result copy(int code, @NotNull String msg) {
Intrinsics.checkNotNullParameter(msg, "msg");
return new Result(code, msg);
}
@NotNull
public String toString() {
return "Result(code=" + this.code + ", msg=" + this.msg + ")";
}
public int hashCode() {
int var10000 = this.code * 31;
String var10001 = this.msg;
return var10000 + (var10001 != null ? var10001.hashCode() : 0);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Result) {
Result var2 = (Result)var1;
if (this.code == var2.code && Intrinsics.areEqual(this.msg, var2.msg)) {
return true;
}
}
return false;
} else {
return true;
}
}
数据类可以继承其他类代码验证
open class Sup{}
interface Base{}
data class Result(var code:Int,val msg:String): Sup(),Base{}
如果想让JVM为数据类生成一个无参构造,那么数据类的主构造中的参数必须都有默认值,示例代码如下:
数据类代码:
data class Result(var code:Int=0,val msg:String=""){}
反编译后生成的java代码:
public final class Result {
...省略一些getter和setter方法
public Result(int code, @NotNull String msg) {
Intrinsics.checkNotNullParameter(msg, "msg");
super();
this.code = code;
this.msg = msg;
}
// $FF: synthetic method
public Result(int var1, String var2, int var3, DefaultConstructorMarker var4) {
if ((var3 & 1) != 0) {
var1 = 0;
}
if ((var3 & 2) != 0) {
var2 = "";
}
this(var1, var2);
}
public Result() {
this(0, (String)null, 3, (DefaultConstructorMarker)null);
}
...省略不相关代码
}
可以看到上的代码JVM会为数据类生成一个没有参数的构造器。
有些常见的属性操作,我们可以通过委托方式,让它实现,例如:
interface Base{
fun print()
}
class BaseImpl(var x: Int):Base{
override fun print(){
print(x)
}
}
// Derived 的 print 实现会通过构造函数的b对象来完成
class Derived(b: Base): Base by b
with
不怎么使用,因为它确实不防空run
和 apply
Unit : Kotlin 中Any的子类, 方法的返回类型为Unit时,可以省略;
Void:Java中的方法无法回类型时使用,但是不能省略;
Nothing:任何类型的子类,编译器对其有优化,有一定的推导能力,另外其常常和抛出异常一起使用;
使用场景是用来修饰函数,使用了 infix 关键字的函数称为中缀函数,使用时可以省略 点表达式和括号。让代码看起来更加优雅,更加语义化。
原理不过是编译器在语法层面给与了支持,编译为 Java 代码后可以看到就是普通的函数调用。
kotlin 的很多特性都是在语法和编译器上的优化。
kotlin存在四种可见性修饰符,默认是public。 private、protected、internal、public
构造方法默认是public修饰,可以使用可见性修饰符修饰constructor关键字来改变构造方法的可见性。
kotlin调用java的时候 如果java返回值可能为 null 那就必须加上@nullable 否则kotlin无法识别,也就不会强制你做非空处理,一旦java返回了 null 那么必定会出现null指针异常,加上@nullable 注解之后kotlin就能识别到java方法可能会返回null,编译器就能会知道,并且强制你做非null处理,这也就是kotlin的空安全
给一个包含N个组件函数(component)的对象分解为替换等于N个变量的功能,而实现这样功能只需要一个表达式就可以了。
例如
有时把一个对象 解构 成很多变量会很方便,例如:
val (name, age) = person
这种语法称为 解构声明 。一个解构声明同时创建多个变量。 我们已经声明了两个新变量: name 和 age,并且可以独立使用它们:
println(name)
println(age)
一个解构声明会被编译成以下代码:
val name = person.component1()
val age = person.component2()
Kotlin里使用关键 inline 来表示内联函数,那么到底什么是内联函数呢,内联函数有什么好处呢?
fun test() {
//多次调用 sum() 方法进行求和运算
println(sum(1, 2, 3))
println(sum(100, 200, 300))
println(sum(12, 34))
//....可能还有若干次
}
/**
*求和计算
*/
fun sum(vararg ints: Int): Int {
var sum = 0
for (i in ints) {
sum += i
}
return sum
}
在测试方法 test() 里,我们多次调用了 sum() 方法。为了避免多次调用 sum() 方法带来的性能损耗,我们期望的代码类似这样子的:
fun test() {
var sum = 0
for (i in arrayOf(1, 2, 3)) {
sum += i
}
println(sum)
sum = 0
for (i in arrayOf(100, 200, 300)) {
sum += i
}
println(sum)
sum = 0
for (i in arrayOf(12, 34)) {
sum += i
}
println(sum)
}
3次数据求和操作,都是在 test() 方法里执行的,没有之前的 sum() 方法调用,最后的结果依然是一样的,但是由于减少了方法调用,虽然代码量增加了,但是性能确提升了。那么怎么实现这种情况呢,一般工具类有很多公共方法,我总不能在需要调用这些公共方法的地方,把代码复制一遍吧,内联就是为了解决这一问题。
定义内联函数:
inline fun sum(vararg ints: Int): Int {
var sum = 0
for (i in ints) {
sum += i
}
return sum
}
如上所示,用关键字 inline 标记函数,该函数就是一个内联函数。还是原来的 test() 方法,编译器在编译的时候,会自动把内联函数 sum() 方法体内的代码,替换到调用该方法的地方。查看编译后的字节码,会发现 test() 方法里已经没了对 sum() 方法的调用,凡是原来代码里出现sum() 方法调用的地方,出现的都是 sum() 方法体内的字节码了。
inline fun test(inlined: () -> Unit) {...}
这里有个问题需要注意,如果在内联函数的内部,函数参数被其他非内联函数调用,就会报错,如下所示:
//内联函数
inline fun test(inlined: () -> Unit) {
//这里会报错
otherNoinlineMethod(inlined)
}
//非内联函数
fun otherNoinlineMethod(oninline: () -> Unit) {}
要解决这个问题,必须为内联函数的参数加上 noinline 修饰,表示禁止内联,保留原有函数的特性,所以 test() 方法正确的写法应该是:
inline fun test(noinline inlined: () -> Unit) {
otherNoinlineMethod(inlined)
}
fun test() {
innerFun {
//return 这样写会报错,非局部返回,直接退出 test() 函数。
return@innerFun //局部返回,退出 innerFun() 函数,这里必须明确退出哪个函数,写成 return@test 则会退出 test() 函数
}
//以下代码依旧会执行
println("test...")
}
fun innerFun(a: () -> Unit) {
a()
}
非局部返回我的理解就是返回到顶层函数,如上面代码中所示,默认情况下是不能直接 return 的,但是内联函数确是可以的。所以改成下面这个样子:
fun test() {
innerFun {
return //非局部返回,直接退出 test() 函数。
}
//以下代码不会执行
println("test...")
inline fun innerFun(a: () -> Unit) {
a()
}
}
也就是说内联函数的函数参数在调用时,可以非局部返回,如上所示。那么 crossinline 修饰的 lambda 参数,可以禁止内联函数调用时非局部返回。
fun test() {
innerFun {
return //这里这样会报错,只能 return@innerFun
}
//以下代码不会执行
println("test...")
inline fun innerFun(crossinline a: () -> Unit) {
a()
}
}
inline fun startActivity() {
startActivity(Intent(this, T::class.java))
}
使用时直接传入泛型即可,代码简洁明了:
startActivity()
kotlin中若存在主构造函数,其不能有代码块执行,init起到类似作用,在类初始化时侯执行相关
的代码块。
处理集合时性能损耗的最大原因是循环。集合元素迭代的次数越少性能越好。
我们写个例子:
list.map { it ++ }
.filter { it % 2 == 0 }
.count { it< 3 }
反编译一下,你会发现:Kotlin编译器会创建三个while循环。
Sequences 减少了循环次数
Sequences提高性能的秘密在于这三个操作可以共享同一个迭代器(iterator),只需要一次循环即可完成。
Sequences允许 map 转换一个元素后,立马将这个元素传递给 filter操作 ,而不是像集合(lists) 那样,等待所有的元素都循环完成了map操作后,用一个新的集合存储起来,然后又遍历循环从新的集合取出元素完成filter操作。
Sequences 是懒惰的
上面的代码示例,map、filter、count 都是属于中间操作,只有等待到一个终端操作,如打印、sum()、average()、first()时才会开始工作,不信?你跑下下面的代码?
val list = listOf(1, 2, 3, 4, 5, 6)
val result = list.asSequence()
.map { println("--map"); it * 2 }
.filter { println("--filter");it % 3 == 0 }
println("go~")
println(result.average())
扩展:Java8 的 Stream(流) 怎么样呢?
list.asSequence()
.filter { it < 0 }
.map { it++ }
.average()
list.stream()
.filter { it < 0 }
.map { it++ }
.average()
stream的处理效率几乎和Sequences一样高。它们也都是基于惰性求值的原理并且在最后(终端)处理集合。
说一下个人理解吧。先列出协程几个特点:
优点:
对于方法传入的参数直接通过if判断,例如:
fun a(tag: String?, type: String) {
if (tag != null && type != null) {
// do something
}
}
还有就是
a?.let{}
a?.also{}
a?.run{}
a?.apply{}
然后接着有一个疑问,假如同时判断两个变量,写成:
a?.let{
b?.let {
//do something
}
}
同:都是顶级父类
异:成员方法不同
Any只声明了toString()、hashCode()和equals()作为成员方法。
我们思考下,为什么 Kotlin 设计了一个 Any ?
当我们需要和 Java 互操作的时候,Kotlin 把 Java 方法参数和返回类型中用到的 Object 类型看作 Any,这个 Any 的设计是 Kotlin 兼容 Java 时的一种权衡设计。
所有 Java 引用类型在 Kotlin 中都表现为平台类型。当在Kotlin 中处理平台类型的值的时候,它既可以被当做可空类型来处理,也可以被当做非空类型来操作。
试想下,如果所有来自 Java 的值都被看成非空,那么就容易写出比较危险的代码。反之,如果 Java 值都强制当做可空,则会导致大量的 null 检查。综合考量,平台类型是一种折中的设计方案。
不可隐式转换
在Java中从小到大,可以隐式转换,数据类型将自动提升。下面以int为例
这么写是ok的
int a = 2312;
long b = a;
//那么在Kotlin中
//隐式转换,编译器会报错
val anInt: Int = 5
val ccLong: Long = anInt
//需要去显式的转换,下面这个才是正确的
val ddLong: Long = anInt.toLong()
对于如下集合
val list = mutableListOf("a", "b", "c", "d", "e", "f", "g")
// kotlin中集合的遍历方式有下面几种
// 1、 通过解构的方式,可以方便的获取index和value
for ((index, value) in list.withIndex()){
println("index = $index , value = $value")
}
// 2、 … 左闭右闭 [,]
for (i in 0 .. list.size - 1){
println("index = $i , value = ${list[i]}")
}
// 3、 until 左闭右开 [,)
for (i in 0 until list.size){
println("index = $i , value = ${list[i]}")
}
// 4、 downTo 递减的循环方式 左闭右闭 [,]
for (i in list.size - 1 downTo 0){
println("index = $i , value = ${list[i]}")
}
// 5、带步长的循环step可以和其他循环搭配使用
for (i in 0 until list.size step 2){
println("index = $i , value = ${list[i]}")
}
// 6、指定循环次数 it就代表了当前循环的计数,从0开始下面的语句循环了10次 每次的计数分别是 0,1…9
repeat(10){
println(it)
}
// 7、不需要数据的下标,直接for循环list中的每个item
for (item in list){
println(item)
}