Ответ 1
Давайте дадим двум версиям разные имена, чтобы их было немного легче говорить:
object Foo1 {
class Bar1
}
object Foo2 {
object Bar2 {
class Baz2
}
}
Теперь, если вы посмотрите на файлы классов, вы увидите, что компилятор Scala создал класс Foo1
. Когда вы запустите javap -v
на Foo1$Bar1
, вы увидите, что этот класс указан как класс-оболочка:
InnerClasses:
public static #14= #2 of #13; //Bar1=class Foo1$Bar1 of class Foo1
Это именно то, что произойдет со статическим вложенным классом в Java, поэтому компилятор Java с удовольствием компилирует new Foo1.Bar1()
для вас.
Теперь посмотрите на вывод javap -v
для 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$
Теперь охватывающий класс Foo2$Bar2$
, а не Foo2$Bar2
(на самом деле компилятор Scala даже не генерирует Foo2$Bar2
, если вы не добавите класс-компаньон для object Bar2
). Компилятор Java ожидает, что статический внутренний класс Baz2
охватывающего класса Foo2$Bar2$
будет называться Foo2$Bar2$$Baz2
с двумя знаками доллара. Это не соответствует тому, что было на самом деле (Foo2$Bar2$Baz2
), поэтому оно не соответствует new Foo2.Bar2.Baz2()
.
Java отлично согласна принимать знаки доллара в именах классов, и в этом случае, поскольку он не может понять, как интерпретировать Foo2$Bar2$Baz2
как внутренний класс какого-то рода, он позволит вам создать экземпляр с new Foo2$Bar2$Baz2()
. Так что обходной путь, просто не очень красивый.
Почему компилятор Scala обрабатывает Foo1
и Bar2
по-разному (в том смысле, что Bar2
не получает класс Bar2
) и почему класс включения, указанный в InnerClasses
атрибут для Baz2
имеет знак доллара на конце, а для Bar1
- нет? Я понятия не имею. Но эта разница - вам просто нужно немного больше подробностей, чтобы увидеть ее с помощью javap
.