hot*_*key 52 code-structure kotlin
在Kotlin中,具有至少一个参数的函数可以定义为常规非成员函数,也可以定义为扩展函数,其中一个参数是接收者.
对于范围界定,似乎没有区别:两者都可以在类和其他函数内部或外部声明,并且两者都可以或不可以具有相同的可见性修饰符.
语言参考似乎不建议在不同情况下使用常规函数或扩展函数.
所以,我的问题是:扩展功能何时优于常规非成员功能?当常规的扩展?
foo.bar(baz, baq)VS bar(foo, baz, baq).
它只是一个函数语义的提示(接收器肯定是焦点)或是否有使用扩展函数使代码更清洁/开辟机会的情况?
Jay*_*ard 57
扩展功能在少数情况下很有用,在其他情况下是强制性的:
惯用案例:
当您想要增强,扩展或更改现有API时.扩展函数是通过添加新功能来更改类的惯用方法.您可以添加扩展功能和扩展属性.请参阅Jackson-Kotlin模块中的示例,以便在ObjectMapper类中添加简化处理TypeReference和泛型的方法.
为无法在a上调用的新方法或现有方法添加null安全性null.例如,String of的扩展函数String?.isNullOrBlank()允许您甚至在nullString 上使用该函数,而无需先进行自己的null检查.函数本身在调用内部函数之前进行检查.请参阅Nullable Receiver的扩展文档
强制性案件:
如果需要接口的内联默认函数,则必须使用扩展函数将其添加到接口,因为在接口声明中不能这样做(内联函数必须final在接口中当前不允许).当您需要内联的已知函数时,这非常有用,例如来自Injekt的此代码
如果要为for (item in collection) { ... }当前不支持该用法的类添加支持.您可以添加一个iterator()遵循for循环文档中描述的规则的扩展方法- 即使返回的迭代器类对象也可以使用扩展来满足提供next()和的规则hasNext().
将运算符添加到现有类中,例如+和*(#1的特化,但不能以任何其他方式执行此操作,因此是必需的).请参阅运营商重载的文档
可选案例:
您希望控制调用者可以看到某些内容的范围,因此您只能在允许调用可见的上下文中扩展该类.这是可选的,因为您可以只允许一直看到扩展名. 对于范围扩展函数,请参阅其他SO问题中的答案
您有一个界面,您希望简化所需的实现,同时仍然为用户提供更简单的帮助程序功能.您可以选择为接口添加默认方法以提供帮助,或使用扩展功能添加接口的非预期实现部分.一个允许覆盖默认值,另一个不允许(扩展与成员的优先级除外).
当您想要将功能与功能类别相关联时; 扩展函数使用它们的接收器类作为查找它们的位置.它们的名称空间成为可以触发它们的类(或类).而顶级函数将更难找到,并将填充IDE代码完成对话框中的全局名称空间.您还可以修复现有的库名称空间问题.例如,在Java 7中,您拥有Path该类,并且很难找到该Files.exist(path)方法,因为它奇怪地名称间隔.该功能可以直接放在上面Path.exists().(@kirill)
优先规则:
扩展现有类时,请记住优先级规则.它们在KT-10806中描述为:
对于当前上下文中的每个隐式接收器,我们尝试成员,然后是本地扩展函数(也是具有扩展函数类型的参数),然后是非本地扩展.
扩展功能与安全呼叫操作员配合得非常好?..如果您希望函数的参数有时是null,而不是提前返回,则使其成为扩展函数的接收者.
普通功能:
fun nullableSubstring(s: String?, from: Int, to: Int): String? {
if (s == null) {
return null
}
return s.substring(from, to)
}
Run Code Online (Sandbox Code Playgroud)
扩展功能:
fun String.extensionSubstring(from: Int, to: Int) = substring(from, to)
Run Code Online (Sandbox Code Playgroud)
通话网站:
fun main(args: Array<String>) {
val s: String? = null
val maybeSubstring = nullableSubstring(s, 0, 1)
val alsoMaybeSubstring = s?.extensionSubstring(0, 1)
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,两者都做同样的事情,但是扩展功能更短,并且在呼叫站点上,立即清楚结果将是可空的.
至少有一种情况必须使用扩展函数 - 调用链,也称为“流畅风格”:
foo.doX().doY().doZ()
Run Code Online (Sandbox Code Playgroud)
假设您想使用自己的操作从 Java 8 扩展 Stream 接口。当然,您可以为此使用普通函数,但它看起来很丑陋:
doZ(doY(doX(someStream())))
Run Code Online (Sandbox Code Playgroud)
显然,您想为此使用扩展函数。此外,您不能使普通函数中缀,但您可以使用扩展函数来做到这一点:
infix fun <A, B, C> ((A) -> B).`|`(f: (B) -> C): (A) -> C = { a -> f(this(a)) }
@Test
fun pipe() {
val mul2 = { x: Int -> x * 2 }
val add1 = { x: Int -> x + 1 }
assertEquals("7", (mul2 `|` add1 `|` Any::toString)(3))
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
4238 次 |
| 最近记录: |