45 votes

Utiliser Scala de Java: passer des fonctions comme 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 car Java n'autorise 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.

67voto

Seth Tisue Points 9090

Dans l' scala.runtime package, il y a des classes abstraites nommé AbstractFunction1 et ainsi de suite pour les autres arities. Pour l'utilisation de Java vous avez seulement besoin de remplacer apply, comme ceci:

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

Mise à jour (2014): Si vous êtes sur Java 8 et que vous voulez utiliser Java 8 lambda syntaxe pour cela, découvrez https://github.com/scala/scala-java8-compat. Et/ou, restez à l'écoute pour Scala 2.12.

27voto

Jean-Philippe Pellet Points 25240

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 l'article "Interop entre Java et Scala" de Daniel Spiewak .

7voto

paradigmatic Points 20871

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 JFunction objets 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);
      }
    };

}
 

0voto

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X