Passer une fonction Scala à une méthode Java 8

18

Le code Scala suivant fonctionne et peut être transmis à une méthode Java qui attend une fonction. Existe-t-il une façon plus propre de procéder? Voici ma première passe:

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] = ???
}

Voici mon deuxième passage - il utilise un wrapper générique pour l'interface de fonction Java-8 pour simplifier la syntaxe Scala:

// 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)

Pouvons-nous faire mieux?

À titre de suivi, y a-t-il une chance que Scala utilise un jour le code d'opération dynamique d'invocation comme Java 8 le fait pour son application de fonction de première classe? Est-ce que tout fonctionnera comme par magie, ou faudra-t-il encore une conversion syntaxique?

GlenPeterson
la source

Réponses:

10

Vous pouvez rendre la conversion implicite:

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

Vous ne devriez pas avoir besoin de remplacer composeet andThen, mais peut-être que le compilateur Scala ne connaît pas encore les méthodes d'interface par défaut de Java 8. (EDIT: cela devrait fonctionner en 2.10.3.)

En outre, vous devriez pouvoir attribuer des lambdas Scala (c'est x => ...-à- dire ) à Functionet tout autre type SAM dans Scala 2.11, et des lambdas Java 8 à Function1. (EDIT: cela a en fait été ajouté dans Scala 2.12, pas 2.11.)

Alexey Romanov
la source
you should be able to assign Scala lambdas- Cela signifie que, dans Scala 2.11, une fonction Scala peut être passée comme argument pour un Lambda Java8?
Kevin Meredith
La possibilité de le passer comme argument dépend du type de lambda; si c'est par exemple, java.util.function.Consumer[scala.Function1[Integer, String]]cela peut être fait avant 2.11 aussi.
Alexey Romanov