扩展函数(extension functions)是Kotlin与Java之间的一个关键区别,自语言首个版本以来便受到C#的启发。它们允许向现有类添加新方法,而无需继承或修改原始类。这显著增强了代码的表达性和简洁性。
在Java和许多其他语言中,通过使用继承或装饰者模式,无法向已存在的类(例如,String或List)添加方法。Kotlin通过扩展函数机制解决了这个缺陷。
许多库类(例如,String、Int、List)无法直接修改。通常需要额外的方法来提高代码的可读性并避免逻辑重复。然而,扩展函数并不会修改实际的类——它们是静态创建的,并且在内部不能访问private/protected成员。
在Kotlin中,扩展函数的声明方式如下:
fun String.lastChar(): Char = this[this.length - 1]
现在,可以在任何String上调用这个方法:
"Kotlin".lastChar() // 返回'n'
扩展函数可以在类的子类中被重写吗?
不可以。扩展函数不是类的成员,无法通过override来重写:它们的静态性质即使在子类中也会显现出来——编译器根据表达式的类型而不是对象的实际类型选择函数。
open class Base class Derived : Base() fun Base.foo() = "base" fun Derived.foo() = "derived" fun printFoo(b: Base) { println(b.foo()) } val d = Derived() printFoo(d) // 输出"base"
扩展函数可以改变对象的状态吗?
扩展函数无法访问private/protected状态,但如果类型是可变的(例如,List或自定义类),它们可以通过公共方法修改可用的外部状态。
fun MutableList<Int>.addTwice(elem: Int) { add(elem) add(elem) }
扩展函数会与同名的类成员冲突吗?
如果扩展函数的名称与实际方法的名称相同,优先级归类成员所有。仅当成员不存在或不可见时,才会调用扩展函数。
class Foo { fun bar() = "member" } fun Foo.bar() = "extension" val f = Foo() println(f.bar()) // "member"
开发者通过扩展标准String添加特定于项目的格式功能,并广泛使用——其他开发者未能注意到这不是String类的标准操作。
优点:
缺点:
通过实用方法扩展标准类,该方法被明确定位在utils包中并得到良好文档化。使用限制在明确协商的代码区域内。
优点:
缺点: