0

I'm provided a class Foo which does some work():

open class Foo() {      
    fun work(x: T) {
        // Effects taking place here  
    }
}

And I am also provided (but by someone else) a method useJob() which consumes an object of interface type Bar having a single method doJob().

fun useJob(bar: Bar)

interface Bar {
    fun doJob(x: T)
}

It turns out Foo.work() does the job expected by useJob(). However, in order to have useJob() calling work(), I need to write something like that:

useJob(object : Foo(), Bar { fun doJob(x: T) { work(x) } })

Is there any way to use a lambda instead of this blob?

EDIT: @jrtapsell comment made me realize Foo is actually open.

3
  • Can foo be open? Commented Oct 23, 2017 at 15:37
  • @jrtapsell yes it can Commented Oct 23, 2017 at 16:09
  • Do you need the Bar interface, or could you just have fun useJob(bar: (T) -> Unit)? Commented Oct 24, 2017 at 16:21

4 Answers 4

4

Well the easiest way to achieve this would be by using a factory method that creates a instance of Bar and accepts a function call:

fun job(func: (Param) -> Unit) : Bar = object: Bar { 
   override fun doJob(x: Param) = func(x)
}

then you can use

useJob( job { Foo().work(it) } )
Sign up to request clarification or add additional context in comments.

Comments

1

If Bar were defined in Java, you could write

useJob { Foo().work(x) }

or

val foo = Foo()
useJob { foo.work(x) }

to avoid constructing Foo() every time in case useJob calls its argument multiple times.

But

note that this feature works only for Java interop; since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported.

Without moving Bar to Java, I'd go with joecks' solution or define an overload of useJob (possibly as extension method). Which is better depends on how many methods like useJob you have and how many uses for each.

Comments

1

It's a problem, that useJob is expecting an interface instead of a function type directly. This way you can only do:

val bar = object : Bar {
    override fun doJob(x: String) = Foo().work(x)
}

useJob(bar)

Comments

0

You could do it like this:

// Mock class
class Param

// Provided code
open class Foo<T> {
    fun work(x: T) {
        // Effects taking place here
    }
}

fun useJob(bar: Bar) {}

interface Bar {
    fun doJob(x: Param)
}

// New code
object FooBar: Foo<Param>(), Bar {
    override fun doJob(x: Param) = work(x)
    fun use() = useJob(this)
}

fun x() {
    FooBar.use()
}

It requires a bit more code for the FooBar object, but it cleans up the call sites.

1 Comment

Nice code. Actually Param was a typo in Bar and T is a defined type, not a genericity parameter.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.