Someone asked me today how to write a service so its API can be used from Java. His problem is that he is using Scala's collection classes but it is not possible to use their methods from Java. This is because when using some features of Scala, the encoded method has a name that is not legal in Java (using '$' in particular).

 

For example:

trait API {
  def service: Map[String, String]
}

 

If we call the method 'service' from Java, there's very little we can do with the result.

 

The common wisdom is to create an adapter class that exposes Java consumable classes. Say, create a JavaAPI interface and JavaAPIImpl. This is very tedious and also tends to become very verbose since if another API interface returns our API interface it also requires wrapping so it will return a JavaAPI interface.

 

An alternative can be to use the @bridge annotation introduced in Scala 2.9.0. Currently, the @bridge annotation is private, but I hope it will become public.

 

The annotation is a very clever idea to achieve backward compatability while preventing new code from using deprecated methods. When a method is annotated with @bridge, the compiler will generate the bytecode for this method. However, when compiling other code, it will pretend that this method does not exist. So if we have a method 'foo', and we want to deprecate it, we annotate it with @bridge. The method continues to exist in bytecode, so classes that were compiled with the older version of the code continue to work. However, compiling new classes creates an error and requires us to modify the code and stop using this method.

 

We can use this annotation to make a method that is visible only to Java classes:

import collection.JavaConversions
import annotation.bridge

trait API {
  def service: Map[String, String]

  @bridge def javaService = service.asJava
}

 

So when I compile Scala code that uses this trait, the compiler will not allow calls to 'javaService'. However, the method will be available in bytecode, so Java code can call it.

 

 

3

Comments

Good insight. I also liked http://www.tikalk.com/java/blog/type-safe-builder-scala-using-type-constraints I'm trying to create a builder library in Scala accessible from Java as well. See http://stackoverflow.com/questions/6337444/builder-library-for-scala-and-java   I'm wondering how to use your type safe builders example from Java and if @Bridge can help.   Non-type-safe builder: Yes, callable from Java... class BuilderN private (i: Int) { def this() = this(-1) def withProperty(i: Int) = new BuilderN(i) def build = println(i) } //javap output public class BuilderN extends java.lang.Object implements scala.ScalaObject{ public BuilderN withProperty(int); public void build(); public BuilderN(); }   Type-safe builder, How to call this from Java? @Bridge perhaps? sealed trait TBoolean sealed trait TTrue extends TBoolean sealed trait TFalse extends TBoolean class Builder[HasProperty <: TBoolean] private(i: Int) { protected def this() = this(-1) def withProperty(i: Int)(implicit ev: HasProperty =:= TFalse) = new Builder[TTrue](i) def build(implicit ev: HasProperty =:= TTrue) = println(i) } //javap output public class Builder extends java.lang.Object implements scala.ScalaObject{ public Builder withProperty(int, scala.Predef$$eq$colon$eq); public void build(scala.Predef$$eq$colon$eq); public Builder(); } object Builder { def apply() = new Builder[TFalse] }    

I don't think it is possible to use the builder from Java and maintain the type safety (of not being able to call build if property wasn't set). The reason is that the use of implicits relies on the "cooperation" of the compiler that knows the type of HasProperty at the call site and finds the appropriate implicit. The Builder class itself does not know the type of HasProperty because of type erasure.   Using @bridge, you can create a non-type-safe version of build that is accessed by the Java code: @bridge def build = build(null)   (the use of null is only because I know ev is not used in the body of build. Otherwise, you need to supply a function that returns TTre)   Please note that @bridge is not yet available outside of the scala  package