Lamada表达式简化
当一个函数传入的参数为lamada并且这个参数是最后一个,那可以将lamada表达式放在括号外面。
viewList<View>.forEach({view:View
print(view.toString)
})
由于kotlin可以进行类型判断,所以可以简化为:
viewList<View>.forEach({view->
print(view.toString)
})
如果lamada函数有且仅有一个参数,可以继续简化为:
viewList<View>.forEach({
print(it.toString)
})
将lamada表达式移到括号外面,括号省略,最终为:
viewList<View>.forEach{
print(it.toString)
}
infix 关键字
当一个函数逻辑比较简单且只有一个参数,该函数可以用infix关键字修饰:
class A{
infix fun add(a:Int):Int{
return 2+a
}
}
var a = A()
var b = a add 3
print(b) //b=5
inline关键字
- 用inline关键字修饰的函数就是内联函数。它的工作原理就是把它的内联函数体复制到它的调用处。它的意义就是减少一层调用栈。
inline fun log(text:String){
Log.e("TAG",text)
}
fun main(){
log("Hello")
}
通过字节码文件可以看出,添加了inline关键字后,main函数里面调用的是
Log.e("TAG","Hello")
而如果不用inline关键字修饰,main函数里面调用的是
log("Hello")
也就是说前者的栈中只有两个函数(main() 、e() ),而后者的栈中有三个函数(main()、log()、e() )。
- 内联函数会增加编译器的工作量, 内联函数在配合函数传入参数类型是函数类型的时候效果最好(函数类型编译后会额外创建一个对象)
inline fun measureTime(action:()->Unit){
var beforeTime = System.currentTimeMillis()
action()
var afterTime = System.currentTimeMillis()
println("the total time is ${afterTime - beforeTime}")
}
fun main(){
measure{
println("Hello")
}
}
通过反编译上面代码的字节码文件可以看出,如果使用inline关键字修饰,main函数执行measureTime函数会将measureTime函数里面的代码复制插入过来
public static final void main() {
int $i$f$measureTimes = false;
long beforeTime$iv = System.currentTimeMillis();
int var3 = false;
String var4 = "Hello";
boolean var5 = false;
System.out.println(var4);
long afterTime$iv = System.currentTimeMillis();
String var9 = "the total time is " + (afterTime$iv - beforeTime$iv);
boolean var8 = false;
System.out.println(var9);
}
如果不使用inline关键字修饰,不仅会增加一个调用栈,而且会创建一个传入函数参数的对象(Function0,因为java的函数是无法作为参数传递的,所以会将函数包装成一个类来进行传递),如果是一个多次重复的操作,就会创建大量对象
public static final void measureTimes(@NotNull Function0 action) {
Intrinsics.checkParameterIsNotNull(action, "action");
long beforeTime = System.currentTimeMillis();
action.invoke();
long afterTime = System.currentTimeMillis();
String var5 = "the total time is " + (afterTime - beforeTime);
boolean var6 = false;
System.out.println(var5);
}
data类
data class Student(var name:String , var age:Int) {
}
kotlin会自动生成get&set方法,相比较Java,kotlin的data类还会生成一个copy函数,将字节码文件反编译可以看到:
public class Student {
......
public final Student copy(@NotNull String name, int age) {
Intrinsics.checkParameterIsNotNull(name, "name");
return new Student(name, age);
}
}
var student = Student()
var studentCopy = student.copy()
print(student == studentCopy) //true 两个等号调用equals函数,而copy重写了equals函数
print(student === studentCopy) //false 三个等号比较的是地址值
解构
在java中一次调用只能返回一个值,如果需要多个值,就得将它们包装成一个对象,在使用的时候再从这个类中取出多个值来,依次进行赋值
data class Response(var code:Int, var message:String, var student:Student){
fun excute():Response{
val code = 200
val message = "请求成功"
val student = Student("张三",20)
return Response(code,message,student)
}
}
fun main(){
val response = excute()
val code = response.code
val message = response.message
val student = response.student
}
将data类的字节码文件进行反编译,会发现有和成员变量相对应的component函数
public class Response {
......
@NotNull
public final int component1() {
return this.code;
}
public final String component2() {
return this.message;
}
public final Student component3() {
return this.student;
}
}
通过解构的方法,可以将代码优化为:
fun main(){
val(code,message,student) = excute()
// code = excute().component1()
// message = excute().component2()
// student = excute().component3()
print(code)
print(message)
print(student)
}
实际上就是通过对应的component方法进行赋值,与构造相反
函数嵌套
当一个函数只在一个地方用到并且也不希望在别的地方调用,可以进行函数的嵌套:
fun A(){
var a = 1
fun B(){
print(a)
}
}
内部函数可以访问外部函数的参数,每次调用外部函数都会生成内部函数的对象,因此外部函数在频繁调用的时候不要使用嵌套函数
注解
class BaseApplication : Application(){
override fun attachBaseContext(context:Context?){
super.attachBaseContext(context)
this.currentApplication = context
}
companion object{
@get:JvmName(currentApplication)
@JvmStatic
lateinit var currentApplication:Context
private set
fun init(){
......
}
}
}
- 由于kotlin会自动帮我们生成var变量的get和set方法,如果我们不希望set方法被调用,可以通过private set来私有化set方法。
- 在kotlin中没有static关键字,因此用伴生类companion object来代替static进行使用,在kotlin类中,可以通过BaseApplication.currentApplication或者BaseApplication.init()来进行调用,而在java中,只能通过BaseApplication.Componion.init()来调用函数,而无法对currentApplication变量进行调用,若想进行调用,需要添加@JvmStatic注解
- @get:JvmName()注解是修改调用get方法的函数名,注解后可以直接在java中调用BaseApplication.currentApplication而不需要调用BaseApplication.getCurrentApplication(),@get表示生效的范围是get方法,如果是@file表示生效的范围是当前文件
//文件名为Display.kt
@file:JvmName("DisPlayUtils")
fun dp2px(){
......
}
注解前在外部文件调用dp2px方法:DisplayKt.dp2px(), 注解后在外部的调用方法为:DisplayUtils.dp2px()
函数参数默认值
object Utils{
@JvmStatic
fun toast(message:String?){
toast(message,Toast.LENGTH_SHORT)
}
@JvmStatic
fun toast(message:String?, duration:Int){
Toast.makeText(mContext, message?:"", Toast.LENGTH_SHORT).show();
}
}
通过上面可以看到,java中的函数重载存在着很多重复的地方,而为了代码简洁,在kotlin中可以使用函数参数默认值
object Utils{
@JvmStatic
fun toast(message:String?){
toast(message,Toast.LENGTH_SHORT)
}
@JvmStatic
fun toast(message:String?, duration:Int){
Toast.makeText(mContext, message?:"", Toast.LENGTH_SHORT).show();
}
}
通过上面可以看到,java中的函数重载存在着很多重复的地方,而为了代码简洁,在kotlin中可以使用函数参数默认值
object Utils{
@JvmOverloads
@JvmStatic
fun toast(message:String?, duration:Int = Toast.LENGTH_SHORT){
Toast.makeText(mContext, message?:"", Toast.LENGTH_SHORT).show();
}
}
Utlils.toast("success") //不加@JvmOverloads在kotlin中不会报错而在java中调用会报错
@JvmOverloads注解会针对默认参数生成重载方法,如果不加该注解在kotlin中没问题,但是再java中只传一个参数会报错
扩展函数
- 假设有一个将dp转px的方法
fun dp2px(value:Float):Float{
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,value,Resources.getSystem().displayMetrics)
}
那我们在调用这个方法的时候就是
view.width = Utils.dp2px(10f)
可以看出这种调用方法不是很方便代码阅读,而在我们没有办法更改源码的前提下,就需要用到kotlin提供的扩展函数,将上述工具类的方法进行改造后如下:
fun Float.dp2px():Float{
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,this,Resources.getSystem().displayMetrics)
}
即在扩展函数名字前加上需要添加扩展函数的类,用“.”进行分隔,改造后的扩展函数的调用就是:
view.width = 10f.dp2px()
需要注意的是,若扩展函数和成员函数一模一样,那么总是会执行成员函数。
扩展属性
同扩展函数类似,我们也可以在不改变源码的前提下给一个类添加成员属性
val ViewGroup.firstChild:View
get() = getChildAt(0)
抽象属性
- 在java中只有抽象方法,在kotlin中可以创建抽象属性
interface BaseView<T>{
val presenter:T
}
by关键字
- 代理委托
假如现在有一个需求,有一个token属性,每次改变的时候都要缓存到本地,取的时候也先从本地取,那我们可以这样写:
var token:String
set(value){
CacheUtils.save("token",value)
}
get(){
return CacheUtils.get("token")
}
但是如果有好多的token的话,就会非常麻烦,这个时候我们就可以用到委托代理(未完待续…)
var token1:String
set(value){
CacheUtils.save("token",value)
}
get(){
return CacheUtils.get("token")
}
var token2:String
......
var token3:String
......
标准函数
- 又叫作用域函数,他的作用是将一些逻辑连贯的代码放到同一个组用于当中的,这样可以增加代码的可读性。
- apply 特别适合对一个对象初始化的时候做附加操作。
- let 配合空判断的时候比较合适
- also
- run
未完待续…