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

本文围绕Android Kotlin展开,介绍了顶层函数、顶层属性、扩展函数及扩展属性。顶层函数和属性可置于文件顶层,无需附属类,Java调用有对应转换规则。扩展函数能在不继承时扩展类功能,有特定可见性,Java调用需传入接收者对象,且扩展是静态解析的。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

android Kotlin系列:

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()
复制代码
扩展函数的可见性

        在扩展函数中,可以直接访问被扩展类的方法和属性。但扩展函数不允许你打破对象的封装性,扩展函数不能访问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关键字。
  • 扩展可以毫无副作用给原有库的类增加属性和方法。

参考文献:

转载于:https://juejin.im/post/5cebff51e51d45777540fd37

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值