转载

Kotlin知识归纳(三) —— 顶层成员与扩展

Kotlin知识归纳(一) —— 基础语法

Kotlin知识归纳(二) —— 让函数更好调用

Kotlin知识归纳(三) —— 顶层成员与扩展

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

顶层函数

Kotlin认为,根本不需要创建这些无意义的类。可以直接将函数放在代码文件的顶层,不用附属于任何一个类。

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

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该顶层函数,即可使用。

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文件下的所有顶层函数都编译为这个类的静态函数。

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

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

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

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

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

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

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

顶层属性

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

@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()
复制代码

扩展函数的可见性

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

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

    public void daqi(){
    }

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

对其该类进行扩展:

Kotlin知识归纳(三) —— 顶层成员与扩展

public的方法可以正常访问,但凡用 private 修饰的属性或方法,无法在扩展函数中被调用。

Java调用扩展函数

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

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

扩展函数daqiJava#extensionMethod()被转换为一个相同名称的 静态函数 。函数 第一个参数变成接受者类型 ,后面才是原函数的参数列表。

扩展是静态解析的

在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)
    }
复制代码
原文  https://juejin.im/post/5cebff51e51d45777540fd37
正文到此结束
Loading...