14

Let's say we have the following class structure in Scala.

object Foo {
  class Bar
}

We can easily construct Bar in Java with new Foo.Bar(). But everything changes when we add an extra level of nested classes.

object Foo {
  object Bar {
    class Baz
  }
}

Somehow, it's no longer possible to construct the most inner class Baz in Java. Looking at the javap output, I can't see any significant difference between first (2 levels) and second cases (3 levels). Generated code looks pretty reasonable to me.

2 levels:

public class Foo$Bar { ... }

3 levels

public class Foo$Bar$Baz { ... }

With that said, what's the difference between 2-level vs. 3-level nested Scala classes when they accessed from Java?

1 Answer 1

14

Let's give the two versions different names to make them a little easier to talk about:

object Foo1 {
  class Bar1
}

object Foo2 {
  object Bar2 {
    class Baz2
  }
}

Now if you look at the class files, you'll see that the Scala compiler has created a Foo1 class. When you run javap -v on Foo1$Bar1, you'll see that that class is listed as the enclosing class:

InnerClasses:
     public static #14= #2 of #13; //Bar1=class Foo1$Bar1 of class Foo1

This is exactly what would happen with a static nested class in Java, so the Java compiler is perfectly happy to compile new Foo1.Bar1() for you.

Now look at the javap -v output for Foo2$Bar2$Baz2:

InnerClasses:
     public static #16= #13 of #15; //Bar2$=class Foo2$Bar2$ of class Foo2
     public static #17= #2 of #13; //Baz2=class Foo2$Bar2$Baz2 of class Foo2$Bar2$

Now the enclosing class is Foo2$Bar2$, not Foo2$Bar2 (in fact the Scala compiler doesn't even generate a Foo2$Bar2 unless you add a companion class for object Bar2). The Java compiler expects a static inner class Baz2 of a enclosing class Foo2$Bar2$ to be named Foo2$Bar2$$Baz2, with two dollar signs. This doesn't match what it's actually got (Foo2$Bar2$Baz2), so it says no to new Foo2.Bar2.Baz2().

Java is perfectly happy to accept dollar signs in class names, and in this case since it can't figure out how to interpret Foo2$Bar2$Baz2 as an inner class of some kind, it'll let you create an instance with new Foo2$Bar2$Baz2(). So that's a workaround, just not a very pretty one.

Why does the Scala compiler treat Foo1 and Bar2 differently (in the sense that Bar2 doesn't get a Bar2 class), and why does the enclosing class listed in the InnerClasses attribute for Baz2 have a dollar sign on the end, while the one for Bar1 doesn't? I don't really have any idea. But that's the difference—you just need a little more verbosity to see it with javap.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.