Specify receiver on lambda literal without type in

2019-08-28 12:28发布

问题:

Suppose I want a lambda expression which has the type of Foo.()->Unit

Obviously I can specify it by relying on type inference, such that the lambda captures the recipient type: val myLambda: Foo.()->Unit = { ... }

My intuition was that I could do something like: val myLambda = Foo.{ ... }

But that doesn't appear to work. It seems weird that I can't express a lambda value of a particular type without depending on type inference from the lhs.

Is there a syntax I'm just not aware of?

回答1:

There is no syntax that does precisely what you're looking for. As you've already laid it out, you can create a lambda that has Foo as its receiver either by creating it as you're passing it into a function that takes such a lambda:

fun useFooExtension(fooExtension: Foo.() -> Unit) {
    fooExtension(Foo())
}

useFooExtension {
    println(this is Foo)
}

Or by specifying the type of the variable explicitly, like so:

var foo1: Foo.() -> Unit = { println(this is Foo) }

However, you can declare a lambda that takes a Foo as its first parameter, with this explicit lambda syntax (the variable type is only here for explanation purposes, it's otherwise redundant and is inferred):

var foo2: (Foo) -> Unit = { foo: Foo -> println(foo is Foo) }

And these two types of lambdas are actually cross-assignable, because the extension on Foo really just takes that Foo as its first parameter under the hood as well. So you can do all of these things:

foo1 = foo2
foo2 = foo1

useFooExtension(foo1)
useFooExtension(foo2)

There is one difference between the two types: if you want to call them with a Foo instance, you can only call foo1 as an extension, but you can call both of them as if they took a Foo as their first parameter. So these are all valid calls:

Foo().foo1()

foo1(Foo())
foo2(Foo())


标签: kotlin