导读大纲 1.1.1 从 Java 调用扩展函数 1.1.2 扩展函数无法重载
1.1.1 从 Java 调用扩展函数
在编译器底层下,扩展函数是一种静态方法 ,它接受接收器对象作为第一个参数 调用它不涉及创建适配器对象 或任何其他运行时开销 这使得从 Java 使用扩展函数变得非常简单 调用静态方法 并传递接收器对象实例 即可 与其他顶级函数 一样 包含该方法的 Java 类的名称 由声明该函数的文件名决定 关于顶级函数和顶级属性–传送门 <1> 比方说,lastChar扩展函数是在 StringUtil.kt 文件 中声明的 那么在Java中调用时就需要使用"StringUtilKt"类名 关于如何使用JvmName注解修改类名–传送门
char c = StringUtilKt. lastChar ( "Java" ) ;
该扩展函数被声明为顶级函数 ,因此编译为静态方法 <1> 可以从 Java 中静态导入 lastChar 方法 从而简化使用 , 只需 lastChar(“Java”) 即可 注意使用的导入方式"import static xxx"
import static strings. StringFunctions. lastChar;
public class Example { public static void main ( String[ ] args) { System. out. println ( lastChar ( "Java" ) ) ; }
}
扩展函数的静态特性 还意味着子类中不能重载扩展函数
1.1.2 扩展函数无法重载
在 Kotlin 中,方法重载与成员函数一样有效 ,但不能重载扩展函数 <1> 假设有两个类: View 和 Button Button 是 View 的子类,它重载父类中的 click 函数 <2> 要实现这一点,可以使用 open 修饰符标记View和click , 以允许重载 并使用 override 修饰符在子类中提供一个实现
open class View { open fun click ( ) = println ( "View clicked" )
}
class Button: View ( ) { override fun click ( ) = println ( "Button clicked" )
}
如果你声明一个 View 类型的变量 <1> 那么你就可以在该变量中存储 Button 类型的值 因为 Button 是 View 的子类型 <2> 如果在该变量上调用常规方法(如 click) 而该方法在 Button 类中被重载 ,则将使用 Button 类的重载实现
fun main ( ) { val view: View = Button ( ) view. click ( )
}
但对于扩展函数来说,情况并非如此 扩展函数不是类的一部分 ,它们是在类的外部声明 的比如, 在 View 和 Button 类之外 定义 View.showOff() 和 Button.showOff() 扩展函数 即使可以为基类及其子类 定义具有相同名称和参数类型的扩展函数 <1> 但调用的函数取决于变量声明的静态类型 , 该类型在编译时确定 在对 view 变量调用 showOff 时,即使值的实际类型是 Button 也会调用View类型的showOff方法 ,因为view变量的编译时类型是"View"
fun View. showOff ( ) = println ( "I'm a view!" )
fun Button. showOff ( ) = println ( "I'm a button!" )
fun main ( ) { val view: View = Button ( ) view. showOff ( )
}
回忆一下上面所说的扩展函数在 Java 中被编译为静态函数 接收器对象是第一个参数 , Java 也会以同样的方式调用指定类型的扩展函数
class Demo { public static void main ( String[ ] args) { View view = new Button ( ) ; ExtensionsKt. showOff ( view) ; }
}
综上所述,如果类中的成员函数与扩展函数的签名相同 ,则成员函数总是优先 集成开发环境也会警告 您, 扩展函数已被成员函数覆盖