Reputation: 8407
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
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
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