Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing Scala nested classes from Java

Tags:

java

scala

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?

like image 341
Vladimir Kostyukov Avatar asked Jun 12 '15 17:06

Vladimir Kostyukov


People also ask

How do I access nested class?

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.

Can nested class be accessed outside?

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.

Can you nest classes in Java?

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.

How do you access members of an inner class?

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.


1 Answers

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.

like image 128
Travis Brown Avatar answered Oct 07 '22 21:10

Travis Brown