我正在做一些有趣的编码,我很想知道我应该使用哪一个.尝试了两个,他们给了我相同的结果.那么,两者之间的区别是什么?
例子:
fun Any?.foo() = this != null
fun <T> T?.foo() = this != null
Run Code Online (Sandbox Code Playgroud)
实际的功能有点复杂,它实际上根据对象的实际类型做了一些事情(比如when有一些选项)
第二个函数为您提供了在这种特殊情况下不使用的机会:它将接收器的类型捕获到类型参数中T,以便您可以在签名中的其他位置使用它,如参数类型或返回值类型,或在功能体中.
作为一个非常合成的例子,listOf(this, this)第二个函数内部将被输入为List<T?>,保留项类型与接收器类型相同的知识,而第一个函数中的相同表达式将是a List<Any?>.
第一个函数不允许您一般使用接收器类型来存储此类型的项目,接受与参数相同类型的其他项目或在函数的返回值类型中使用接收器类型,而第二个函数允许所有这些.
这些功能是从运行的角度等同,因为仿制药从JVM字节擦除时的代码被编译,所以你将无法确定类型T的依赖于它的运行时间和行为,除非你转换函数的inline函数带有reified类型参数.
作为一个非常重要的特殊情况,将类型从调用站点捕获到类型参数允许高阶函数T在其签名中使用另一个函数.标准库具有一组确定范围功能的(run,apply,let,also),其显示的差.
假设签名also没有使用泛型,看起来像这样:
fun Any?.also(block: (Any?) -> Unit): Any? { ... }
Run Code Online (Sandbox Code Playgroud)
可以在任何对象上调用此函数,但是它的签名不会显示它是传递给block函数并从函数返回的接收器对象- 编译器将无法确保类型安全,例如,允许在没有类型检查的情况下调用receiver接收器对象的成员:
val s: String = "abc"
// won't compile: `it` is typed as `Any?`, the returned value is `Any?`, too
val ss1: String = (s + s).also { println(it.length) }
// this will work, but it's too noisy
val ss2: String = (s + s).also { println((it as String).length) } as String
Run Code Online (Sandbox Code Playgroud)
现在,捕获类型参数正是表明它在所有三个地方都是相同类型的方式.我们修改签名如下:
fun <T : Any?> T.also(block: (T) -> Unit): T { ... }
Run Code Online (Sandbox Code Playgroud)
编译器现在可以推断出类型,知道T它出现的地方是相同的类型:
val s: String = "abc"
// OK!
val ss: String = (s + s).also { println(it.length) }
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
324 次 |
| 最近记录: |