Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement Scala apply method in Java

Tags:

java

scala

I want to call some Java code from Scala code. I would like to use Scala's apply construct, so I can call it like this:

val led = OutPin(0)

instead of:

val led = new OutPin(0)

I naively implemented an additional apply method in my Java code like this:

public class OutPin {

    public OutPin(int pinNumber) {
    }

    public OutPin apply(int pinNumber) {
        return new OutPin(pinNumber);
    }
}

This does not make my Scala code (first line above) compile, and instead gives me an error:

Object OutPin is not a value

What is the correct way to implement Scala's apply method in Java?

like image 824
Jeroen Kransen Avatar asked Dec 25 '13 10:12

Jeroen Kransen


People also ask

How do you call a Scala method from a Java class?

Calling methods on Scala instances work exactly as in Java.ScalaClass scalaClass = new ScalaClass(true); assertEquals(2, scalaClass. plus(1, 1)); Note that Scala classes cannot have static methods. The convention is to put methods of static nature on companion objects.

What is Scala apply method?

Scala List apply() method with example. The apply() method is utilized to select an element in the list by its index. Return Type: It returns an element from the given list by its index which is present in the apply method as argument.

Can Java use Scala code?

A Scala trait with no method implementation is just an interface at the bytecode level and can thus be used in Java code like any other interface.

What is Apply method?

The apply() method calls the specified function with a given this value, and arguments provided as an array (or an array-like object).


2 Answers

Your problem is not with the apply method per-se, but with trying to implement a Scala singleton object in Java.

I think (but am not certain) that this is very difficult, perhaps even impossible, by design.

Consider a very, very simple case:

object Obj;

This compiles to two JVM bytcode files, Obj$.class and Obj.class. In theory, it should be easy to just inspect the bytecode of those two classes, and reexpress the same thing in Java. The basic structure of Scala singleton objects is very, very simple:

  1. For a singleton object Obj, a Obj.class and Obj$.class must be generated
  2. The Obj$ class must have a public final static field of type Obj$ called MODULE$, which will be initialized on class initialization refer to the singleton object. In Scala, calls to Obj.foo() get mapped to Obj$.MODULE$.foo() [...if Obj had a method called foo(), that is!]
  3. The Java compiler doesn't know anything about these Scala generated class pairs, so for Java interop, the Obj class contains static functions that just forward to a call of a method of the same name and signature on Obj$.MODULE$.

That sounds complicated, but it's really not so much. It's trivial to write a pair of Java classes that goes this far. But the Scala compiler (2.10.3) still won't recognize the pair as constituting a Scala singleton. Diving into the bytecode of a Scala-compiler generated singleton, you'll see that there are details that are hard to express in legal Java. [Hint: javap -c -p -s -v <fully-qualified-class-name>]

For example, the final static MODULE$ field is initialized indirectly by the static initializer. The static initializer just constructs an Obj$ object, without directly assigning it. The assignment occurs within the private constructor. That's illegal in Java: blank static finals must be certainly initialized in the static initializer, and cannot be assigned in code (like the private constructor) that might potentially be called from outside the initializer and multiple times. The Scala compiler generates bytecode that respects the blank final semantics (because the private constructor is only called once), but exceeds the Java compiler's ability to verify those semantics. So this code would be rejected if expressed in Java.

Also, the Obj class (the version without the terminal dollar sign) includes an annotation of type ScalaSig, which looks quite complicated and would be hard to reproduce by hand (in Java or in Scala), at least for those of us unsure exactly how this annotation works.

I don't know exactly what the Scala compiler looks for before deciding to treat a pair of classes as a "value", that is a valid Scala singleton object, but Scala's designers have chosen not to make it easy, despite the simplicity of the basic scheme. Probably they wish to preserve the ability to reorganize how scala singleton objects translate to bytecode. Letting Java programmers synthesize scala singleton objects would effectively render the current scheme a permanent part of Scala's public API.

Note that writing an ordinary, non-singleton class whose instances have an apply(...) method and so can be called like functions is easy from Java and works fine. Here's a Java cat:

public class Cat {
    public String apply( int i ) {
        return "Meow: " + i;
    }
}

Here's a use of Scala's sugared apply:

Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_45).
Type in expressions to have them evaluated.
Type :help for more information.

scala> val Morris = new Cat;
Morris: Cat = Cat@6b4feafa

scala> Morris(8)
res0: String = Meow: 8
like image 129
Steve Waldman Avatar answered Oct 06 '22 17:10

Steve Waldman


If you want an apply method that is usable in Scala you should implement it on the scala an side using object to wrap your java class with the same name of the class which instantiate it

object OutPin {

   def apply(pinNumber :Int) = new OutPin(pinNumber)

}
like image 44
Arnon Rotem-Gal-Oz Avatar answered Oct 06 '22 19:10

Arnon Rotem-Gal-Oz