Kotlin知识概括(三) —— 顶层成员与扩展

前序

        在Java项目中,多多少少都存在以Utils结尾的Java类。其内部并没有任何状态和实例函数,只有一堆与该名称相关的静态属性或静态方法。该类只是做为一种容器存储着静态属性和静态方法。java

顶层函数

        Kotlin认为,根本不须要建立这些无心义的类。能够直接将函数放在代码文件的顶层,不用附属于任何一个类。android

在com.daqi包中的daqi.kt文件中定义顶层函数joinToString()bash

package com.daqi

@JvmOverloads
fun <T> joinToString(collection: Collection<T>,
                      separator:String = ",",
                      prefix:String = "",
                      postfix:String = ""):String{
    val result = StringBuilder(prefix)
    for ((index,element) in collection.withIndex()){
        if (index > 0)
            result.append(separator)
        result.append(element)
    }
    result.append(postfix)
    return result.toString()
}
复制代码

        在Kotlin中,顶层函数属于包内成员,包内能够直接使用,包外只须要import该顶层函数,便可使用。app

        Kotlin和Java具备很强互操做性,若是让Java调用顶层函数该怎么调用呢?先看一下顶层函数编译成Java是什么样的:函数

public final class DaqiKt {
   @NotNull
   public static final String joinToString(Collection collection, String separator,
                    String prefix,String postfix) {
   }
}
复制代码

        编译器将顶层函数所在的文件名daqi.kt做为类名DaqiKt,生成对应的类文件。该kt文件下的全部顶层函数都编译为这个类的静态函数。post

so,若是在Java中调用Kotlin的顶层函数时,须要对其的文件名转换为对应的类名,再进行调用。ui

DaqiKt.joinToString(new ArrayList<>(),"",",","");
复制代码

        若是想规定kt文件转换为Java类时的类名,可使用@file:JvmName()注解进行修改。将其放在文件的开头,位于包名以前:this

@file:JvmName("StringUtils")
package com.daqi

fun <T> joinToString(...){
	...
}
复制代码

就可使用特定的类名在Java中调用对应的顶层函数。spa

StringUtils.joinToString(new ArrayList<>(),"",",","");
复制代码

顶层属性

        既然有顶层方法,应该也有顶层属性。和顶层函数同样,属性也能够放在文件的顶层,不附属与任何一个类。这种属性叫顶层属性。.net

@file:JvmName("StringUtils")
package com.daqi

val daqiField :String = "daqi"
复制代码

        顶层属性和其余任意属性同样,都提供对应的访问器(val 变量提供getter,var 变量提供getter 和 setter)。也就是说,当Java访问该顶层属性时,经过访问器进行访问的。

StringUtils.getDaqiField();
复制代码

经过反编译查看其转换为Java的样子:

@NotNull
private static final String daqiField = "daqi";

@NotNull
public static final String getDaqiField() {
    return daqiField;
}
复制代码

顶层属性被定义为私有的静态对象,并配套了一个静态访问器方法。

        若是须要定义public的静态变量,能够用const关键字修饰该变量。(仅适用于基础数据类型和String类型的属性)

在反编译的文件中能够看到,静态属性变成public,且没有了具体的静态访问器。

//Kotlin
const val daqiField :String = "daqi"

//Java
public static final String daqiField = "daqi";
复制代码

扩展函数

        Kotlin能够在无需继承的状况下扩展一个类的功能,而后像内部函数同样直接经过对象进行调用。扩展函数这个特性能够很平滑与现有Java代码进行集成。

        声明一个扩展函数,须要用一个接收者类型也就是被扩展的类型来做为他的前缀。而调用该扩展函数的对象,叫做接收者对象。接收者对象用this表示,this无关紧要。

fun String.lastChar():Char{
    return this.get(this.length - 1)
}

//调用扩展函数
"daqi".lastChar()
复制代码

扩展函数的可见性

        在扩展函数中,能够直接访问被扩展类的方法和属性。但扩展函数不容许你打破对象的封装性,扩展函数不能访问privateprotected的成员。具体什么意思呢,先定义一个java类:

public class daqiJava {
    private String str = "";
    public String name = "";

    public void daqi(){
    }

    private void daqi(String name){
    }
}
复制代码

对其该类进行扩展:

        public的方法能够正常访问,但凡用privateprotected修饰的属性或方法,没法在扩展函数中被调用。

Java调用扩展函数

        回到Kotlin和Java交互性的问题,Java如何调用扩展函数的呢?这时候又要一波反编译:

public static final void extensionMethod(daqiJava $receiver,String string) {
    
}
复制代码

        扩展函数daqiJava#extensionMethod()被转换为一个相同名称的静态函数。函数第一个参数变成接受者类型,后面才是原函数的参数列表。也就是说Java调用扩展函数时,须要先传入对应的接收者对象,再传入该扩展函数的参数。

扩展是静态解析的

        在JVM语言的多态中,被重写方法的调用依据其调用对象的实际类型进行调用。但扩展函数是静态分发的,即意味着扩展函数是由其所在表达式中的调用者的类型来决定的。

咱们都知道Button是View的子类,同时为View和Button定义名为daqi的扩展函数。

val view:View = Button()
view.daqi()
复制代码

        此时调用的是View的扩展函数,即便它实质是一个Button对象。由于扩展函数所在的表达式中,view是View类型,而不是Button类型。

扩展函数其余特性

  • 扩展函数与顶层函数相似,在Java层进行调用时,依据其所在的文件名做为类名,其做为静态函数,存储在该类中。(也支持@file:JvmName("")进行)

  • 在扩展函数中,除了能够调用接收者类型的成员函数和成员属性外,还能够调用该类的扩展函数。

  • 若是一个类的成员函数与扩展函数拥有相同的方法签名,成员函数会被优先使用。

  • 扩展函数实际上是静态函数,扩展函数不能被子类重写。但子类仍能够调用父类的扩展函数。

扩展属性

        扩展属性不能有初始化器,它们的行为只能由显式提供的 getters/setters 定义。由于没有地方对它进行存储,不可能给现有的Java对象实例添加额外的属性。只是用属性的语法对接受者类型进行扩展。

声明一个扩展常量:

val String.lastChar:Char
	get() = get(length - 1)
复制代码

声明一个扩展变量:

var StringBuffer.lastChar:Char
    get() = get(length - 1)
    set(value:Char){
        this.setCharAt(length - 1,value)
    }
复制代码

总结:

  • 顶层函数和扩展函数均可以去掉以”Utils“结尾的静态方法容器类。
  • 顶层函数和顶层属性提供全局的方法和属性,不须要任何对象实例进行调用。
  • 扩展函数须要接受者对象实例来进行调用。
  • 顶层属性是等价于私有的静态对象。
  • 若想获取基本类型和String类型的公有静态对象,在顶层属性定义时,添加const关键字。
  • 扩展能够毫无反作用给原有库的类增长属性和方法。

参考资料:

android Kotlin系列:

Kotlin知识概括(一) —— 基础语法

Kotlin知识概括(二) —— 让函数更好调用

Kotlin知识概括(三) —— 顶层成员与扩展

Kotlin知识概括(四) —— 接口和类

Kotlin知识概括(五) —— Lambda

Kotlin知识概括(六) —— 类型系统

Kotlin知识概括(七) —— 集合

Kotlin知识概括(八) —— 序列

Kotlin知识概括(九) —— 约定

Kotlin知识概括(十) —— 委托

Kotlin知识概括(十一) —— 高阶函数

Kotlin知识概括(十二) —— 泛型

Kotlin知识概括(十三) —— 注解

Kotlin知识概括(十四) —— 反射

相关文章
相关标签/搜索