18

The following Scala code works and can be passed to a Java method expecting a function. Is there a cleaner way to do this? Here's my first pass:

val plusOne = new java.util.function.Function[Int,Int] {
  override def apply(t:Int):Int = t + 1

  override def andThen[V](after:function.Function[_ >: Int, _ <: V]):
    function.Function[Int, V] = ???

  override def compose[V](before:function.Function[_ >: V, _ <: Int]):
    function.Function[V, Int] = ???
}

Here's my second pass - it uses a generic wrapper for the Java-8 Function interface to make the Scala syntax simpler:

// Note: Function1 is Scala's functional interface,
// Function (without the 1) is Java 8's.
case class JavaFunction1[U,V](f:Function1[U,V]) extends Function[U,V] {
  override def apply(t: U): V = f(t)
  override def compose[T](before:Function[_ >: T, _ <: U]):
    Function[T, V] = ???
  override def andThen[W](after:Function[_ >: V, _ <: W]):
    Function[U, W] = ???
}

val plusOne = JavaFunction1((x:Int) => x + 1)
val minusOne = JavaFunction1((x:Int) => x - 1)

Can we do better?

As a follow-up, is there any chance that Scala will someday use the invoke-dynamic op-code as Java 8 does for its first-class function application? Will that make everything magically work, or will there still need to be a syntactic conversion?

10

You can make the conversion implicit:

implicit def toJavaFunction[U, V](f:Function1[U,V]): Function[U, V] = new Function[U, V] {
  override def apply(t: U): V = f(t)

  override def compose[T](before:Function[_ >: T, _ <: U]):
    Function[T, V] = toJavaFunction(f.compose(x => before.apply(x)))

  override def andThen[W](after:Function[_ >: V, _ <: W]):
    Function[U, W] = toJavaFunction(f.andThen(x => after.apply(x)))
}

implicit def fromJavaFunction[U, V](f:Function[U,V]): Function1[U, V] = f.apply

You shouldn't actually need to override compose and andThen, but maybe Scala compiler doesn't know about Java 8 default interface methods yet. (EDIT: it should work in 2.10.3.)

Also, you should be able to assign Scala lambdas (i.e. x => ...) to Function and any other SAM types in Scala 2.11, and Java 8 lambdas to Function1. (EDIT: this actually got added in Scala 2.12, not 2.11.)

  • you should be able to assign Scala lambdas - This means that, in Scala 2.11, a Scala function can be passed as an argument for a Java8 Lambda? – Kevin Meredith Nov 8 '16 at 2:36
  • Whether it can be passes as an argument depends on the lambda's type; if it's e.g. java.util.function.Consumer[scala.Function1[Integer, String]] then it can be done before 2.11 too. – Alexey Romanov Nov 8 '16 at 6:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.