web-dev-qa-db-fra.com

Utilisation de Scala de Java: passage de fonctions en paramètres

Considérez le code Scala suivant:

package scala_Java
object MyScala {
  def setFunc(func: Int => String) {
    func(10)
  }
}

Maintenant en Java, j'aurais aimé utiliser MyScala comme:

package scala_Java;
public class MyJava {
    public static void main(String [] args) {
        MyScala.setFunc(myFunc);  // This line gives an error
    }
    public static String myFunc(int someInt) {
        return String.valueOf(someInt);
    }
}

Cependant, ce qui précède ne fonctionne pas (comme prévu depuis Java ne permet pas la programmation fonctionnelle). Quelle est la solution de contournement la plus simple pour passer une fonction en Java? Je voudrais une solution générique qui fonctionne avec des fonctions ayant un nombre arbitraire de paramètres.

EDIT: Java 8 a-t-il une meilleure syntaxe que les solutions classiques discutées ci-dessous?

45
Jus12

Vous devez instancier manuellement un Function1 en Java. Quelque chose comme:

final Function1<Integer, String> f = new Function1<Integer, String>() {
    public int $tag() {
        return Function1$class.$tag(this);
    }

    public <A> Function1<A, String> compose(Function1<A, Integer> f) {
        return Function1$class.compose(this, f);
    }

    public String apply(Integer someInt) {
        return myFunc(someInt);
    }
};
MyScala.setFunc(f);

Ceci est tiré de article "Interop entre Java et Scala" de Daniel Spiewak .

27

Dans le scala.runtime package, il existe des classes abstraites nommées AbstractFunction1 et ainsi de suite pour d'autres arités. Pour les utiliser depuis Java il vous suffit de remplacer apply, comme ceci:

Function1<Integer, String> f = new AbstractFunction1<Integer, String>() {
    public String apply(Integer someInt) {
        return myFunc(someInt);
    }
};

Si vous êtes sur Java 8 et que vous souhaitez utiliser Java 8 syntaxe lambda pour cela, consultez https://github.com/ scala/scala-Java8-compat .

67
Seth Tisue

La façon la plus simple pour moi est de définir une interface Java comme:

public interface JFunction<A,B> {
  public B compute( A a );
}

Modifiez ensuite votre code scala, en surchargeant setFunc pour accepter également les objets JFunction tels que:

object MyScala {
  // API for scala
  def setFunc(func: Int => String) {
    func(10)
  }
  // API for Java
  def setFunc(jFunc: JFunction[Int,String]) {
    setFunc( (i:Int) => jFunc.compute(i) )
  }
}

Vous utiliserez naturellement la première définition de scala, mais pourrez toujours utiliser la seconde de Java:

public class MyJava {
  public static void main(String [] args) {
    MyScala.setFunc(myFunc);  // This line gives an error
  }

  public static final JFunction<Integer,String> myFunc = 
    new JFunction<Integer,String>() {
      public String compute( Integer a ) {
        return String.valueOf(a);
      }
    };

}
7
paradigmatic

Voici ma tentative de solution, une petite bibliothèque: https://github.com/eirslett/sc8

Vous enveloppez votre Java 8 lambda dans F(...) puis il est converti en une fonction Scala.

0
Eirik Sletteberg