kotlingenericsconstructorfunctional-interface

Kotlin: Passing constructors to functional interfaces


I saw my friend do this in his codebase, but I'm struggling to figure out how to do it with my class.

Suppose I have the following code:

class JobTest {
  fun interface JobCompiler {
    fun compile(s: String, l: Long): Job
  }
  data class Job(val name: String, val id: Long)
}

fun main() {
  val compiler = JobTest.JobCompiler { s: String, l: Long -> JobTest.Job(s, l) }
  val job = compiler.compile("Tom", 100)

  println("Name: ${job.name}, Id: ${job.id}") // Prints Name: Tom, Id: 100
}

This compiles and works fine, but I'm reading an example from another class where they are able to just pass ::Job, and the other two variables are inferred. meaning, They're able to just write:

  val compiler: JobTest.JobCompiler = ::Job

However, when I try the above I get the following compiler error:

Type mismatch. Required: JobTest.JobCompiler Found: KFunction2<String, Long, JobTest.Job>

How would I fix this code so that I only need to use ::??


Solution

  • That implicit conversion of the functional reference to a function interface implementation is supported for function arguments but not variable/property assignments.

    But you can do:

    val compiler = JobTest.JobCompiler(::Job)
    

    so you’re using it as a function argument (of the implicitly created function for constructing functional interfaces) and then assigning it.

    Every functional interface has an implicit "constructor" (not really a constructor) function where the argument is a functional reference matching the signature of its single abstract function.

    So in your case, there is an implicit function that would look like the following if you explicitly defined it:

    inline fun JobCompiler(block: (String, Long)->Job): JobCompiler = object: JobCompiler {
        fun compile(s: String, l: Long): Job = block(s, l)
    }
    

    I don't think the Kotlin documentation mentions this. They talk about SAM conversions, which also use this implicit constructor, but not about how you can also pass functional references instead of lambdas.