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?
They are accessed using the enclosing class name. For example, to create an object for the static nested class, use this syntax: OuterClass. StaticNestedClass nestedObject = new OuterClass.
Unlike the non-static nested classes, the static nested class cannot directly access the instance variables or methods of the outer class. They can access them by referring to an object of a class.
In Java, it is also possible to nest classes (a class within a class). The purpose of nested classes is to group classes that belong together, which makes your code more readable and maintainable.
As the inner class exists inside the outer class we must instantiate the outer class in order to instantiate the inner class. Hence, to access the inner class, first create an object of the outer class after that create an object of the inner class.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With