Mike Slinn
Mike Slinn

Reputation: 8407

Invoking scala.Function.tupled() from Java

I would like to express all of the following Scala code in Java:

object TupleDemo {
  val tuple = (3, "Hello, world! ")

  /** @return str replicated x times */
  def foo(x: Int, str: String) = str * x

  val tupledFoo1 = (foo _).tupled // partially applied function

  val tupledFoo2 = Function.tupled(foo _) // desugared syntax for same partially applied function
}

object TupleDemoApp extends App {
  import TupleDemo._

  println(tupledFoo1(tuple)) // Hello, world! Hello, world! Hello, world!
  println(tupledFoo2(tuple)) // Hello, world! Hello, world! Hello, world!
}

This is as much as I could figure out for the Java equivalent:

import scala.Function1;
import scala.Function2;
import scala.Tuple2;
import scala.collection.immutable.WrappedString;
import scala.runtime.AbstractFunction2;

public class JavaTupleDemo {
   /** @return str replicated x times */
   static final Function2<Integer, String, String> foo = new AbstractFunction2<Integer, String, String>() {
       public String apply(Integer x, String str) {
           return new WrappedString(str).$times(x);
       }

       // perhaps the types for this method are incorrect?
       public Function1<Tuple2<Integer, String>, String> tupled(Tuple2 tuple2) {
           return null; // what to write here instead of null?
       }
   };

  public static void main(String[] args) {
      // works: Invoke tupled function defined in Scala from Java using Tuple2 defined in Java
      Tuple2<Object, String> tuple = new Tuple2<Object, String>(3, "Hello, World! ");
      System.out.println(TupleDemo.tupledFoo1().apply(tuple));

      // works: Invoke regular function defined in Java from Java
      System.out.println(JavaTupleDemo.foo.apply(3, "Hello, planet! "));

      // stumped: Invoke tupled function defined in Java from Java using both the Scala and the Java Tuple2 instances
  }
}

Upvotes: 2

Views: 1010

Answers (2)

Travis Brown
Travis Brown

Reputation: 139048

tupled is implemented in Function2 (and therefore in AbstractFunction2), so there's no need to define it here—you can just write this:

import scala.Function;
import scala.Function2;
import scala.Tuple2;
import scala.collection.immutable.WrappedString;
import scala.runtime.AbstractFunction2;

public class JavaTupleDemo {
  static final Function2<Integer, String, String> foo =
    new AbstractFunction2<Integer, String, String>() {
      public String apply(Integer x, String str) {
        return new WrappedString(str).$times(x);
      }
    };

  public static void main(String[] args) {
    Tuple2<Integer, String> tuple =
      new Tuple2<Integer, String>(3, "Hello, World! ");

    System.out.println(JavaTupleDemo.foo.tupled().apply(tuple));
    System.out.println(Function.tupled(JavaTupleDemo.foo).apply(tuple));
  }
}

Note that we need to write foo.tupled(), since Java sees this as a method, and that in both cases we get a Function1, so we have to write .apply(tuple), but otherwise this is essentially the same as the Scala version.


To address your question about getting the Integer in the tuple typed as an Object: the easiest way to handle this (assuming you can't or don't want to make it an Integer explicitly on the Scala side) is probably the following:

Tuple2<Object, String> scalaTuple = TupleDemo.tuple();

Tuple2<Integer, String> tuple = new Tuple2<Integer, String>(
  (Integer) scalaTuple._1(),
  scalaTuple._2()
);

I.e., just take the tuple apart, cast the Integer, and put it back together.

Upvotes: 4

Mike Slinn
Mike Slinn

Reputation: 8407

The problem with the answer by Travis Brown is that he did not include all of the code. Here is the complete code, which does not compile.

import scala.Function;
import scala.Function2;
import scala.Tuple2;
import scala.collection.immutable.WrappedString;
import scala.runtime.AbstractFunction2;

public class JavaTupleDemo {
   /** tupled() is implemented in Function2 (and therefore in AbstractFunction2)
    * @return str replicated x times */
   static final Function2<Integer, String, String> foo = new AbstractFunction2<Integer, String, String>() {
       public String apply(Integer x, String str) {
           return new WrappedString(str).$times(x);
       }
   };

  public static void main(String[] args) {
      // Invoke tupled function defined in Scala from Java using Tuple2 defined in Java
      Tuple2<Object, String> tuple = new Tuple2<Object, String>(3, "Hello, World! ");
      System.out.println(TupleDemo.tupledFoo1().apply(tuple));

      // Invoke regular function defined in Java from Java
      System.out.println(JavaTupleDemo.foo.apply(3, "Hello, planet! "));

      // Invoke tupled function defined in Java from Java using both the Scala and the Java Tuple2 instances
      System.out.println(JavaTupleDemo.foo.tupled().apply(tuple));
      System.out.println(Function.tupled(JavaTupleDemo.foo).apply(tuple));
  }
}

The error messages are:

error: method apply in interface Function1<T1,R> cannot be applied to given types;
required: Tuple2<Integer,String>
found: Tuple2<Object,String>
reason: actual argument Tuple2<Object,String> cannot be converted to Tuple2<Integer,String> by method invocation conversion
where T1,R are type-variables:
T1 extends Object declared in interface Function1
R extends Object declared in interface Function1

You cannot simply cast tuple to a Tuple2<Integer, String>. If you try the following:

System.out.println(JavaTupleDemo.foo.tupled().apply((Tuple2<Integer, String>)tuple));
System.out.println(Function.tupled(JavaTupleDemo.foo).apply((Tuple2<Integer, String>)tuple));

The error message is:

error: inconvertible types
required: Tuple2<Integer,String>
found:    Tuple2<Object,String>

The only way to make this compile is to do something really nasty - cast tuple first to an Object, then to a Tuple2<Integer, String>:

System.out.println(JavaTupleDemo.foo.tupled().apply((Tuple2<Integer, String>)((Object)javaTuple)));
System.out.println(Function.tupled(JavaTupleDemo.foo).apply((Tuple2<Integer, String>)((Object)javaTuple)));

Is there a better way to do this?

Upvotes: 0

Related Questions