Iulian Dragos

2

I just spent a wonderful week in California, and attended ScalaDays 2011. One thing that popped up during one of the talks was that 'Java inner classes in Scala are a challenge'. I was pretty surprised since this came from one of the speakers, and someone I'm sure is a very knowledgeable person otherwise. Of course, you can use Java inner classes in Scala. But first..

Using Java inner classes in Java

Let's call nested a class defined inside another class that is static, and let's call inner class a non-static class defined inside another class. Java uses the same syntax to refer to both nested and inner classes: import Outer.Inner. We'll consider the following example throughout this post:

package test;

public class Outer {
    public int base = 10;
    public class Inner {
        public int x = 10 + base;
    }
}

You can use the Inner class either by subclassing Outer, or (less common), by importing and instantiating through an outer instance:

import test.Outer.Inner;
import test.Outer;
...
Outer o = new Outer();
Inner i = o.new Inner();
i.x;

Java does not distinguish between instance and static members, so the import line looks unsurprising. However, the instantiation syntax is rather weird, making the keyword new look like a member of o, instead of making Inner look like a member of o.

An inner class has access to all members of the enclosing class, final and non-final alike. Therefore, whenever an inner class is instantiated it needs to be passed (implicitly) a reference to the outer instance.

Inner classes in Scala

Scala treats inner classes like any class members. Therefore, selection (both for importing and instantiation) proceeds with an outer value and goes on to select the inner type.

  val outer = new Outer
  val inner = new outer.Inner

Note that due to type inference, there was no need to specify the type of inner. What is the type of inner?

  val outer = new Outer
  val inner: outer.Inner = new outer.Inner

We can use the same syntax to import the Inner type in scope: import outer.Inner.

In Scala, types that depend on a value (the object outer in this example) are called path-dependent types. If we had a second instance of Outer, say outer2, the two types would be incompatible:

  val outer2 = new Outer
  val inner2: outer.Inner = new outer2.Inner

We'd get the following error:

outer.scala:7: error: type mismatch;
 found   : Test.outer2.Inner
 required: Test.outer.Inner

This is different behavior from Java, where type Outer.Inner matches any instance of Inner, regardless of the outer object instance. It turns out that Scala does have an equivalent type: Outer#Inner. The hash operator performs a type selection. Unlike Java, Scala reserves the dot exclusively for selection on terms.

  var inner: outer.Inner = new outer.Inner
  var inner2: Outer#Inner = new outer2.Inner

  inner2 = inner // works
  inner = inner2 // fails

And the confusion?

There is still one important difference between the way Scala and Java treat inner classes: mutability. Scala requires paths to be immutable, therefore both instantiation and imports have to mention only vals. The following code doesn't work:

  var outer = new Outer
  val inner = new outer.Inner


outer.scala:9: error: stable identifier required, but outer found.
var inner = new outer.Inner

You may ask yourself why this restriction? It has to do with type members (remember, Scala classes may have abstract type members). If the path to the inner class contains mutable variables, the type of Inner members may change during execution. For example, a field may change from Int to String. In short, it's unsound.

I believe it's this restriction that confuses most people. So what do you do if you need to have mutable outer instances? First of all, you need to use type selection (Outer#Inner) to refer to such types. Second, you need to instantiate them through an immutable alias, for instance by defining a helper method:

  var outer1 = new Outer // outer1 is mutable, we need a factory method
  val inner1 = mkInner(outer1)

  def mkInner(o: Outer): Outer#Inner = new o.Inner // o is immutable

The Outer#Inner syntax is not allowed in imports (it's not a path).

With this observations, it should be always possible to use Java inner classes from Scala. I hope this post clarifies a (sometimes shady) part of the Java and Scala interoperability.

Comments (2) Trackbacks (0)
  1. Hi Iulian. Thanks for writing up this post. Just for clarification, in our talk I did not say you cannot use Java inner classes in Scala. You can take a look at the slides here: http://www.slideshare.net/michael.galpin/scala-on-android-experiences-at-bump-technologies. I listed inner classes as a challenge. I also mentioned that we had been able to workaround the challenge.

    After a lot of experimenting, we came around the # (and the $) syntax as well. One thing you might want to add to your blog post is that you cannot use the # syntax in an import statement, i.e.

    import test.Outer#Inner

    Will not work. Is there an import statement that would work?

    I consider using Java inner classes a challenge because of the lack of documentation. When we had to use them in our code, our first attempt would not compile. We could not figure out what we did wrong based on the message from the compiler. We looked at Scala documentation (online and from the staircase book) and could not figure out the problem. My colleague was discouraged and just decided to write that part of the code in Java. I was more stubborn and figured out how to get it to work in Scala, but it took me awhile to do it.

    • Hi Michael,

      I totally agree with the lack of documentation on Java-Scala interaction, and that’s the main reason I wrote this post. Hopefully people will be able to find it in the future and save them some frustration.

      I’ll update the post in the coming days (I’m away travelling right now), and straighten up the introduction — sorry I disinformed.

      There is indeed no syntax to import Outer#Inner. And the problem I have with the $ syntax is that you’re basically throwing away the compiler knowledge about inner classes and need to pass the outer instance explicitly. Theoretically, identifiers using $ are not supposed to be used in user programs (they’re compiler generated). Of course, it’s good you managed to solve the problem, but hopefully I showed a ‘cleaner’ way.

      thanks for you feedback.

Leave a comment








No trackbacks yet.