高阶函数的参数为
我们习惯于filter
和with
来自kotlin的stdlib:
@Test
fun `filter example`() {
val filtered = listOf("foo", "bar").filter {
it.startsWith("f")
}
assertThat(filtered).containsOnly("foo")
}
@Test
fun `with example`() {
val actual = with(StringBuilder()) {
append("foo")
append("bar")
toString()
}
assertThat(actual).isEqualTo("foobar")
}
尽管filter
使用函数类型参数,而with
使用带有接收器的函数类型参数。因此,传递给filter
的lambda使用it
访问iterable的元素,而传递给with
的lambda使用this
访问StringBuilder。
我的问题:当我声明自己的高阶函数时,是否有经验法则,使用哪种样式(相对于此)?
换句话说: 为什么过滤不是这样设计的?
inline fun <T> Iterable<T>.filter2(predicate: T.() -> Boolean): List<T> = filter { it.predicate() }
如果以这种方式定义,我们将像这样使用它:
@Test
fun `filter2 function type with receiver`() {
val filtered = listOf("foo", "bar").filter2 {
// note: no use of it, but this
startsWith("f")
}
assertThat(filtered).containsOnly("foo")
}
答案 0 :(得分:3)
您根本不总是希望与接收者一起工作。例如,考虑您的filter
直接在元素上工作,则必须在比较中使用this
限定词,然后:
val filtered = listOf("foo", "bar").filter2 {
this == "f"
}
这看起来很奇怪而且不自然。 this
指向什么?
您已将this
的范围更改为指向接收者,并且如果要访问“外部” this
,它将看起来像这样:
this@SomeClass.c =="f"
另一个缺点是您失去了命名参数的可能性。例如,考虑嵌套的lambda。那么it
和this
都不适合。您必须提供自定义名称。
您应该始终考虑是否真的要切换到接收器的示波器。有些情况是完美的用例,尤其是DSLs。对于通常的高阶函数,您根本不想拥有此功能。
我认为很难为此制定一个“规则”,但是作为一个入门者,您可以阅读关于如何在可用范围函数(let
,run
之间进行选择的JetBrains recommends ,also
,apply
,with
):
您是在块中的多个对象上调用方法,还是将上下文对象的实例作为参数传递?如果是这样,请使用允许您按其访问上下文对象的功能之一,而不是此(也可以或让)。如果该块中根本没有使用接收器,也可以使用。
答案 1 :(得分:3)
我的经验法则如下:
只要有很小的可能需要命名我的lambda参数,我就使用(Type) -> Unit
。
如果我确定不会命名(因此从上下文中可以明显看出,我操作的所有内容都是this
),或者我甚至想禁止命名(生成器?),那么我可以使用{ {1}}。
Type.() -> Unit
,with
和apply
都使用第二种方法……对我来说,这很有意义:
run
以下是我使用第二种方法的示例:
with(someString) {
toUpperCase() // operates on someString... already sounds like: "with some string (do) to upper case"
}
someString.run(::println) // run println with someString; actually: someString.run { println(this) } // e.g.: print this [some string]
// actually I find this a bad sample... I usually use run where the thing to be run is from the receiver...
SomeComplexObject().apply {
// apply being similar to a builder
complex1 = 3
complex2 = 4
}.run {
// fully constructed complex object
complexOperationWithoutReturnValue() // this method is part of SomeComplexObject
} // discarding everything here...
使用fun sendMail(from : String, to : String, msgBuilder : MessageBuilder.() -> Unit)
sendMail("from", "to") {
subject("subject")
body("body")
}
或参数(例如it
)只会变得更丑陋,并且不会真正为上下文添加任何内容...