Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

Java and Scala


May 14, 2021 Scala


Table of contents


Java and Scala

Javap

Javap is a tool that comes with the JDK. I t's not JRE, there's a difference here. J avap anticompile class definitions to show you what's in it. The usage is simple

[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait
Compiled from "Scalaisms.scala"
public interface com.twitter.interop.MyTrait extends scala.ScalaObject{
    public abstract java.lang.String traitName();
    public abstract java.lang.String upperTraitName();
}

If you are the underlying controller you can look at the bytecode

[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap -c MyTrait\$class
Compiled from "Scalaisms.scala"
public abstract class com.twitter.interop.MyTrait$class extends java.lang.Object{
public static java.lang.String upperTraitName(com.twitter.interop.MyTrait);
  Code:
   0:   aload_0
   1:   invokeinterface #12,  1; //InterfaceMethod com/twitter/interop/MyTrait.traitName:()Ljava/lang/String;
   6:   invokevirtual   #17; //Method java/lang/String.toUpperCase:()Ljava/lang/String;
   9:   areturn

public static void $init$(com.twitter.interop.MyTrait);
  Code:
   0:   return

}

If you can't figure out why the program doesn't work on Java, look at it with javap!

Class

There are four key points to consider when using Scala classes in Java

  • Class parameters
  • Class constant
  • Class variable
  • Abnormal

We'll build a simple Scala class to demonstrate this series of entities

package com.twitter.interop

import java.io.IOException
import scala.throws
import scala.reflect.{BeanProperty, BooleanBeanProperty}

class SimpleClass(name: String, val acc: String, @BeanProperty var mutable: String) {
  val foo = "foo"
  var bar = "bar"
  @BeanProperty
  val fooBean = "foobean"
  @BeanProperty
  var barBean = "barbean"
  @BooleanBeanProperty
  var awesome = true

  def dangerFoo() = {
    throw new IOException("SURPRISE!")
  }

  @throws(classOf[IOException])
  def dangerBar() = {
    throw new IOException("NO SURPRISE!")
  }
}

Class parameters

  • By default, class parameters are valid Java constructors. This means that you cannot access from outside the class.
  • Declare a class parameter as val/var and the code is the same
class SimpleClass(acc_: String) {
  val acc = acc_
}

This makes it accessible in Java code just like any other constant

Class constant

  • Constant (val) defines a method of acquisition in Java. You can access the value of "val foo" through the method "foo()"

Class variable

  • The variable (var) generates a _$eq You can call it that way
foo$_eq("newfoo");

BeanProperty

You can @BeanProperty and var by using the . T his generates the getter/setter definition. I f you want to generate an isFoo method, use the BooleanBeanProperty annotation. The ugly foo$_eq become

setFoo("newfoo");
getFoo();

Abnormal

Scala does not have a checked exception like Java. T he need to check for exceptions is a philosophical debate that we won't enter, but it's important when you need to catch it in Java. D angerFoo and DangerBar will demonstrate this. This cannot be done in Java

        // exception erasure!
        try {
            s.dangerFoo();
        } catch (IOException e) {
            // UGLY
        }

Java complains that s.dangerFoo has never thrown an IOException exception. We can skip by capturing Throwable, but that's not good.

Conversely, as a good Scala citizen, you can use throws annotations with dignity, as you would in dangerBar. This allows us to continue using the detected exception in Java.

Read on

The full list of Scala annotations that support Java interoperability http://www.scala-lang.org/node/106.

Characteristics

How do you get an interface plus implementation? Let's look at a simple definition of traits

trait MyTrait {
  def traitName:String
  def upperTraitName = traitName.toUpperCase
}

This trait has an abstract method (traitName) and an implementation method (upperTraitName). W hat did Scala build for us? An interface called MyTrait, and an implementation class called MyTrait$class.

MyTrait is what you expect

[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait
Compiled from "Scalaisms.scala"
public interface com.twitter.interop.MyTrait extends scala.ScalaObject{
    public abstract java.lang.String traitName();
    public abstract java.lang.String upperTraitName();
}

MyTrait$class is more interesting

[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap MyTrait\$class
Compiled from "Scalaisms.scala"
public abstract class com.twitter.interop.MyTrait$class extends java.lang.Object{
    public static java.lang.String upperTraitName(com.twitter.interop.MyTrait);
    public static void $init$(com.twitter.interop.MyTrait);
}

MyTrait$class only has static methods with MyTrait instances as parameters. This gives us a hint on how to extend a trait in Java.

Let's try the following first

package com.twitter.interop;

public class JTraitImpl implements MyTrait {
    private String name = null;

    public JTraitImpl(String name) {
        this.name = name;
    }

    public String traitName() {
        return name;
    }
}

We'll get the following errors

[info] Compiling main sources...
[error] /Users/mmcbride/projects/interop/src/main/java/com/twitter/interop/JTraitImpl.java:3: com.twitter.interop.JTraitImpl is not abstract and does not override abstract method upperTraitName() in com.twitter.interop.MyTrait
[error] public class JTraitImpl implements MyTrait {
[error]        ^

We can do it ourselves. But there's a ghostly way.

package com.twitter.interop;

    public String upperTraitName() {
        return MyTrait$class.upperTraitName(this);
    }

All we have to do is put the call proxy on the generated Scala implementation. We can also overwrite it if we like.

Singleton object

A singleton object is how Scala implements static method/singleton mode. I t would be a bit strange to use in Java. None of them have the perfect style to use, but they don't work very well in Scala 2.8

A Scala singleton object is compiled into a class that ends with "$". Let's create a class and a companion object

class TraitImpl(name: String) extends MyTrait {
  def traitName = name
}

object TraitImpl {
  def apply = new TraitImpl("foo")
  def apply(name: String) = new TraitImpl(name)
}

We can naively access Java like this

MyTrait foo = TraitImpl$.MODULE$.apply("foo");

Now you might ask yourself, is this a god horse thing? T his is a normal reaction. Let's take a look at what's actually inside TraitImpl$

local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap TraitImpl\$
Compiled from "Scalaisms.scala"
public final class com.twitter.interop.TraitImpl$ extends java.lang.Object implements scala.ScalaObject{
    public static final com.twitter.interop.TraitImpl$ MODULE$;
    public static {};
    public com.twitter.interop.TraitImpl apply();
    public com.twitter.interop.TraitImpl apply(java.lang.String);
}

In fact, there is no static method in it. I nstead, it is a static member named MODULE$. T he method implementation is delegated to the member. This makes access code ugly, but feasible.

Forwarding Methods

Working with singleton objects in Scala 2.8 becomes a little easier. I f you have a class with a companion object, the 2.8 compiler generates a forwarding method in the companion class. So, if you use 2.8, you can call the traitImpl singleton object's method like this

MyTrait foo = TraitImpl.apply("foo");

The closure function

One of Scala's most important features is the use of functions as first-class citizens. Let's define a class that defines some methods that use functions as arguments.

class ClosureClass {
  def printResult[T](f: => T) = {
    println(f)
  }

  def printResult[T](f: String => T) = {
    println(f("HI THERE"))
  }
}

You can call it like this in Scala

val cc = new ClosureClass
cc.printResult { "HI MOM" }

It's not that easy in Java, but it's not scary. Let's take a look at what ClosureClass is actually compiled into:

[local ~/projects/interop/target/scala_2.8.1/classes/com/twitter/interop]$ javap ClosureClass
Compiled from "Scalaisms.scala"
public class com.twitter.interop.ClosureClass extends java.lang.Object implements scala.ScalaObject{
    public void printResult(scala.Function0);
    public void printResult(scala.Function1);
    public com.twitter.interop.ClosureClass();
}

It's not that scary. " f: sgt; T" is escaped as "Function0" and "f: String sgt; T" is escaped as "Function1". S cala is actually defined from Function0 to Function22 and supports up to 22 parameters. That really should be enough.

Now we just need to figure out how to use these East Easts in Java. We can pass in AbstractFunction0 and AbstractFunction1 from Scala, like this

    @Test public void closureTest() {
        ClosureClass c = new ClosureClass();
        c.printResult(new AbstractFunction0() {
                public String apply() {
                    return "foo";
                }
            });
        c.printResult(new AbstractFunction1<String, String>() {
                public String apply(String arg) {
                    return arg + "foo";
                }
            });
    }

Note that we can use generic parameters.