Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/deanwampler/scalajavainterop
A simple "lightning talk" about Scala-Java Interoperability for the Chicago Scala user group
https://github.com/deanwampler/scalajavainterop
Last synced: about 1 month ago
JSON representation
A simple "lightning talk" about Scala-Java Interoperability for the Chicago Scala user group
- Host: GitHub
- URL: https://github.com/deanwampler/scalajavainterop
- Owner: deanwampler
- Created: 2010-07-15T21:22:08.000Z (over 14 years ago)
- Default Branch: master
- Last Pushed: 2010-07-16T14:38:02.000Z (over 14 years ago)
- Last Synced: 2024-10-12T15:10:57.217Z (2 months ago)
- Language: Java
- Homepage: http://meetup.com/chicagoscala
- Size: 902 KB
- Stars: 2
- Watchers: 3
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Scala-Java Interoperability
Lightning talk at the July 15th, 2010 Chicago-Area Scala Enthusiasts meeting.
Dean Wampler
[GitHub](http://github.com/deanwampler/ScalaJavaInterop)
# Introduction
This talk explores how Scala writes valid byte code and what that means for interoperation with Java. I compiled the code with `sbt` and then used `javap` to examine the byte code signatures.
## References
See the following for more details:
* **Programming Scala**, Chapter 14:
* [Java Interoperability](http://programming-scala.labs.oreilly.com/ch14.html#JavaInterop)
* [Java Interoperability](http://programming-scala.labs.oreilly.com/ch14.html#JavaLibraryInterop)
* Daniel Spiewak's post on [Interop Between Java and Scala](http://www.codecommit.com/blog/java/interop-between-java-and-scala)The notes that follow largely follow the outline of Daniel's blog post.
## Using the Examples
The code is built using `sbt`. (The `# foo` are comments and `$` and `>` are shell and `sbt` prompts, respectively.
$ ./sbt # invoke the provided sbt script
> update # Upate jars
> compile # build the code.
(There are no tests provided.) The class files are written to the `target/scala_2.8.0.RC7/classes/`.Use `javap`, part of the JDK distribution to see how the names, method signatures, etc. are encoded at the byte code level; how they appear to `javac` or `java`. Note the space before the package name `interop`:
javap -classpath target/scala_2.8.0.RC7/classes/ interop.JListMapper
javap -classpath target/scala_2.8.0.RC7/classes/ 'interop.JListMapper$'Note that you need to escape the '$' in the latter example. Otherwise, it is interpreted as the start of a shell variable expansion. I just put the object name in single quotes.
Use `scalap`, part of the Scala distribution to see how the names, method signatures, etc. are reinterpreted as Scala.
scalap -classpath target/scala_2.8.0.RC7/classes/ interop.JListMapper
scalap -classpath target/scala_2.8.0.RC7/classes/ 'interop.JListMapper$'The signatures will look pretty much the same as the original source. Use the open-source tool [jad](http://varaneckas.com/jad) to attempt to reverse engineer the byte code back to working Java. It doesn't always work completely. I'm not sure why, but it might make assumptions that `javac` wrote the byte code and hence not properly understand idiosyncrasies of `scalac` output.
There is one executable in the code.
scala -cp target/scala_2.8.0.RC7/classes/ interop.JFunctionMain foo bar baz
I'll discuss these examples in the following sections.# Character Encoding
Scala allows a wider variety of characters for type and method names than Java's `[_a-zA-Z][_a-zA-Z0-9]*`. So, Scala encodes the extra characters thusly:
trait AllOpChars {
def == : Unit // $eq$eq
def > : Unit // $greater
def < : Unit // $less
def + : Unit // $plus
def - : Unit // $minus
def * : Unit // $times
def / : Unit // $div
def \ : Unit // $bslash
def | : Unit // $bar
def ! : Unit // $bang
def ? : Unit // $qmark
def :: : Unit // $colon$colon
def % : Unit // $percent
def ^ : Unit // $up
def & : Unit // $amp
def @@ : Unit // $at$at
def ## : Unit // $hash$hash
def ~ : Unit // $tilde
}
There appear to be some additional coding conventions for "specialized" type instantiations, e.g., `List[Double]` for `List`. I couldn't find any documentation on these. Feedback welcome!You can see an example of this encoding when you compile `PersonTrait.scala` and run `javap` on it. Look for the `name_$eq` method. This is the compiler-generated setter method `name_=` that allows you to write, e.g.,
myperson.name = "Bubba"
# Classes
Classes declared in Scala vs. Java have almost identical byte code. The Scala generated code will implement a `ScalaObject` interface.
Note that my book and Daniel's blog post refer to an internal `$tag` method required by the `ScalaObject` interface. This method was removed in Scala 2.8.
# Traits vs. Interfaces
Traits with no defined methods or fields are identical to interfaces and can be used interchangeably between Java and Scala. For example, see the `AbstractPerson` in `PersonTrait.scala`.
Traits with methods lead to the generation of a companion `Foo$class` class, which holds the method implementations. For example, see the `Person` in `PersonTrait.scala`. Two class files are generated by `scalac`, `Person.class` for the "interface portion" and a corresponding `Person$class.class` file that contains the method bodies.
See how these are used in Scala and Java in the `Employee.scala` and `Employee.java` files, respectively.
# Generics
Scala type parameters are a superset of Java generics, _e.g.,_ covariant and contravariant subtyping.
trait Function2[-A1, -A2, +R] {
def apply(a1: A1, a2: A2): R
}
Java only lets you specify covariant and contravariant behavior at the _call_ site, not the _definition_ site. Scala gets away with this and other type system enhancements because of type erasure! That maligned feature lets Scala "sneak in" the improved behavior.# "Operators" Are Methods
When you write
val list = 1 :: 2 :: 3 :: Nil
You are actually just calling methods on `List`.abstract class List[+A] {
def ::[B >: A](e: B) = ...
...
}# Higher-Order Functions in Java
Functions in Scala are objects, e.g., a one-argument function is the following.
trait Function1[-T, +R] {
def apply (t: T): R
def toString = ...
def compose[A](g: A => T): A => R = ...
def andThen[A](g: R => A): T => A = ...
}
You can use these in Java! However there's a catch. In 2.7.7, you had to define `apply` and the internal method `$tag`, something like this.Function1 toUpper =
new Function1() {
public String apply(String s) {
return s.toUpperCase();
}
public int $tag() { return 0; }
};In 2.8.0, `$tag` is no longer required, but it also appears that the new `@specialized` annotations cause problems when instantiating anonymous subclasses of the `FunctionN` traits in Java code. You get errors for undefined `andThen` methods, e.g., for type `R` = `Double`.
To work around this, create a concrete subclass of `Function1[String,String]` in Scala code with a default implementation of the `apply` method. Then, in the Java code, create a subclass that overrides `apply` to do the actual work desired. An alternative is to have the default `apply` call an abstract helper method, say `doApply`, then define `doApply` in your Java code subclass (i.e., use the Template Method pattern).
For an example, see `JListMapper` and `JFunctionMain`.
# Interoperation with Java Libraries.
The most important issue you'll encounter, beyond what we've discussed so far, is the fact that some Java libraries, _e.g.,_ the Spring Framework and most IDEs, expect objects to follow JavaBeans conventions:
public class Person {
public Person(...) {...}
public String getName() {...};
public void setName(String s) {...};
// etc.
}
Often these tools use reflection to locate "bean properties" this way. In contrast, consider the corresponding Scala class.class Person(var name: String, ...)
It uses `name` for the getter and `name_=` for the setter, of course. If you need bean methods, use the `@scala.reflect.BeanProperty` on each field, or use `@scala.reflect.BeanInfo` on the class to affect all fields.See the `PersonBean` example in `PersonTrait.scala` and use `javap` on the generated class files, `PersonBean.class` and `PersonBean$class.class`. As written, `getName` and `setName` methods are generated. Try this experiment, remove the assignment for `name`, so that it is pure abstract. Compile again and look at the class files with `javap`. You'll see that just the getter `getName` is defined, not the setter `setName`, even though we declared it as a `var`. I don't know why...