Przekazywanie funkcji Scala do metody Java 8

18

Poniższy kod Scala działa i można go przekazać do metody Java oczekującej funkcji. Czy jest na to czystszy sposób? Oto moja pierwsza przepustka:

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

Oto moje drugie przejście - używa ogólnego opakowania dla interfejsu funkcji Java-8, aby uprościć składnię Scali:

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

Czy możemy zrobić lepiej?

W następstwie, czy jest jakaś szansa, że ​​Scala kiedyś użyje dynamicznego kodu operacyjnego invoke, tak jak Java 8 dla swojej pierwszej funkcji aplikacji? Czy to sprawi, że wszystko magicznie zadziała, czy też nadal będzie trzeba dokonać konwersji syntaktycznej?

GlenPeterson
źródło

Odpowiedzi:

10

Możesz uczynić konwersję domyślną:

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

W rzeczywistości nie powinieneś zastępować composei andThen, ale być może kompilator Scala nie wie jeszcze o domyślnych metodach interfejsu Java 8. (EDYCJA: powinno działać w 2.10.3.)

Ponadto powinieneś być w stanie przypisać lambda Scala (tj. x => ...) Do Functionwszystkich innych typów SAM w Scali 2.11 i lambd Java 8 do Function1. (EDYCJA: to faktycznie zostało dodane w Scali 2.12, a nie 2.11.)

Aleksiej Romanow
źródło
you should be able to assign Scala lambdas- Oznacza to, że w Scali 2.11 można przekazać funkcję Scala jako argument dla Lambda Java8?
Kevin Meredith
To, czy można go podać jako argument, zależy od typu lambda; jeśli tak jest, java.util.function.Consumer[scala.Function1[Integer, String]]to można to zrobić także przed 2.11.
Aleksiej Romanow