Pasar una función Scala a un método Java 8

18

El siguiente código Scala funciona y se puede pasar a un método Java que espera una función. ¿Hay una forma más limpia de hacer esto? Aquí está mi primer pase:

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

Aquí está mi segundo paso: utiliza un contenedor genérico para la interfaz de la función Java-8 para simplificar la sintaxis de 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)

¿Podemos hacerlo mejor?

Como seguimiento, ¿hay alguna posibilidad de que Scala algún día use el código de operación dinámico de invocación como lo hace Java 8 para su aplicación de función de primera clase? ¿Eso hará que todo funcione mágicamente, o todavía tendrá que haber una conversión sintáctica?

GlenPeterson
fuente

Respuestas:

10

Puede hacer que la conversión sea implícita:

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

En realidad, no debería necesitar anular composey andThen, pero tal vez el compilador Scala aún no conoce los métodos de interfaz predeterminados de Java 8. (EDITAR: debería funcionar en 2.10.3.)

Además, debería poder asignar Scala lambdas (es decir x => ...) Functiony cualquier otro tipo de SAM en Scala 2.11, y Java 8 lambdas a Function1. (EDITAR: esto realmente se agregó en Scala 2.12, no 2.11.)

Alexey Romanov
fuente
you should be able to assign Scala lambdas- Esto significa que, en Scala 2.11, ¿se puede pasar una función Scala como argumento para un Lambda Java8?
Kevin Meredith
Si se puede pasar como argumento depende del tipo de lambda; si es, por ejemplo java.util.function.Consumer[scala.Function1[Integer, String]], se puede hacer antes de 2.11 también.
Alexey Romanov