写作本文的目的
kotlin虽然不是java的语法糖,但在作用上确实就是个语法糖,学起来虽然轻松,但是有些概念不经常使用容易埋坑, 多看看kotlin反编译以后的代码 可以加深对 语法糖的 理解。也可以避免用java的写法来写kotlin。熟悉了以后可以更轻松自如的写kotlin味道的kotlin代码。
const val 与 val
object MyObject {
const val t1 = 1
val t2 = 2
}
来看看反编译以后的代码
public final class MyObject {
public static final int t1 = 1;
private static final int t2 = 2;
public static final MyObject INSTANCE;
public final int getT2() {
return t2;
}
private MyObject() {
}
static {
MyObject var0 = new MyObject();
INSTANCE = var0;
t2 = 2;
}
}
从代码中可以看到 访问t1 的时候 是直接通过 field来访问 访问t2 的时候 就是用的 方法了。
所以总结下区别:
const val 是public的 val 是private的,访问效率上 val 要低一些,const val 要高一些。 因为访问val是通过方法,要有一次方法调用。而const val 则是直接访问field 效率更高
== 与 ===
实话说 这个设计我觉得是一个非常失败的设计,都快和js一样了。但既然kotlin这样设计了 我们还是看看为什么这样设计
fun main() {
var user1 = User3("wuyue", "t3")
var user2 = user1.copy()
println(user1 == user2)
println(user1 === user2)
}
data class User3(var username: String, var password: String?)
他的执行结果:
在java中显然是没有=== 这个操作符的。但是kotlin中。 上面的代码 我直接解释成
== 在koltin中代表 对象中的field 是否相等。 === 代表引用是否相等。
来反编译看看
看下copy函数做了啥
看到这应该能明白kotlin的 copy函数 其实就是重新new了一个对象 当然是浅拷贝。
==走的 就是下面这个函数
显然这个eqauls 就是user3的equals了
看到这你应该就能明白kotlin中的 == 与 === 的区别是哪来的了。 同时也可以知道 data class 这种写法
会自动根据我们设定的field来帮我们生成equals方法。
kotlin中的解构
package com.wuyue
data class Response(var code:Int,var message:String)
fun execute():Response{
val code=200
var message="ok"
return Response(code,message)
}
fun main()
{
val(code,message)= execute()
println(code)
println(message)
}
这个代码相信很多人都看得懂, 看main函数里 的第一行代码 这样的写法其实和es6中的解构其实是差不多的 也就是说 我们可以在一个函数调用中 返回多个值 ,这样的写法比我们不停的get出来返回值 要高效的多。 但是注意了这里和go语言中的函数多返回值是不一样的。
我们来看看kotlin中 是如何实现解构的
首先可以看一下:
data class 的写法 生成的bean代码 中 是多了这2个函数的。
然后看看调用时的写法
不用解释了吧。。。所以看到这 你应该可以理解这句话: kotlin虽然不是java的语法糖,但在作用上确实是无限接近于语法糖的作用的
kotlin中神奇的扩展函数
fun main() {
var a = "hello world"
println(a.doubleToString())
}
fun String.doubleToString(): String {
return this.toString() + "__" + this.toString()
}
初学kotlin的都觉得 扩展函数很神奇,比如上面的代码。 扩展了string的函数 增加一个doubleToString的函数, 函数的作用我就不写了,大家一看就懂。
现在来反编译看看 这到底是咋实现的
嗯 越来越像语法糖了。
inline 函数
还是上面的例子
inline fun String.doubleToString2(): String {
return this.toString() + "__" + this.toString()
}
fun main() {
var a = "hello world"
println(a.doubleToString())
println(a.doubleToString2())
}
fun String.doubleToString(): String {
return this.toString() + "__" + this.toString()
}
你看我新增了一个内联函数,这东西干啥的?传说能提高性能?反编译看一看
一眼就看出来,加上inline关键字的地方 在实际函数调用时 其实就是直接调用了inline函数体里面的语句
而没有直接调用函数本身,相对而言可以节省一次函数调用。但是也就仅此而已了。 个人认为少一次函数调用出栈入栈 其实并提高不了多少效率。
反而的这样还会带来代码膨胀。假设你inline函数里面语句非常多,那么你如果在很多地方都调用了这个inline函数 可想而知 你调用的地方就会多出来非常多的代码。 代码膨胀的速度是飞快的。
kotlin支持 函数作为参数的真相
这个又是一个类似于js的功能。我们来看看kotlin实际是怎么做的
class View {
interface OnClickListener {
fun onClick(view: View)
}
fun setOnClickListener(listener: (View) -> Unit) {
}
}
fun main() {
var view = View()
view.setOnClickListener {
onClick(view)
}
}
fun onClick(view: View) {
println("被点击")
}
很像js的闭包了,java中就不可以传递一个函数作为一个参数,但是kotlin可以, 那我们看看是怎么做的
首先反编译可以看出来 其实在定义的时候 还是interface在起作用的。
我们最终调用的时候 也可以看出来 还是遵循的java的规范 传递的是一个实际的对象。并不是函数。
这里大家有可以试试 在回调函数这边增加个inline关键字,看看会发生什么,这里就不演示了
init函数
fun main() {
var p1 = Persont()
}
class Persont constructor() {
init {
println("111")
}
init {
println("222")
}
}
运行以后结果如下:
反编译看一看:
所以说这个地方的init函数 无非就是 当你在kotlin中使用了主构造器的时候 把你的init里面的代码 按照顺序放到构造函数里面,其他没啥。 为什么要这么做? 当然是因为主构造器的写法没有方法体,所以需要这么写,仅此而已。