Java: несколько объявлений класса в одном файле

В Java вы можете определить несколько классов верхнего уровня в одном файле, при условии, что не более одного из них являются общедоступными (см. JLS §7.6). См. Ниже, например.

  • Есть ли чистое название для этой техники (аналогично inner, nested, anonymous)?

  • JLS говорит, что система может обеспечить ограничение, что эти вторичные классы не могут быть referred to by code in other compilation units of the package, например, они не могут рассматриваться как private-private. Это действительно что-то, что меняется между реализациями Java?

например, PublicClass.java:

package com.example.multiple;

public class PublicClass {
    PrivateImpl impl = new PrivateImpl();
}

class PrivateImpl {
    int implementationData;
}

Ответ 1

Мое предложенное имя для этой техники (включая несколько классов верхнего уровня в одном исходном файле) было бы "беспорядочным". Серьезно, я не думаю, что это хорошая идея - вместо этого я использовал бы вложенный тип в этой ситуации. Тогда еще легко предсказать, в каком исходном файле он находится. Я не верю, что существует официальный термин для этого подхода.

Что касается того, действительно ли это происходит между реализациями - я очень сомневаюсь в этом, но если вы избегаете делать это в первую очередь, вам никогда не придется заботиться:)

Ответ 2

javac не запрещает это активно, но имеет ограничение, которое в значительной степени означает, что вы никогда не захотите ссылаться на класс верхнего уровня из другого файла, если он не имеет то же имя, что и файл, в котором он находится.

Предположим, у вас есть два файла: Foo.java и Bar.java.

Foo.java содержит:

  • открытый класс Foo

Bar.java содержит:

  • публичный класс Bar
  • класс Baz

Пусть также говорят, что все классы находятся в одном пакете (и файлы находятся в одном каталоге).

Что произойдет, если Foo.java ссылается на Baz, но не на Bar, и мы пытаемся скомпилировать Foo.java? Ошибка компиляции с ошибкой:

Foo.java:2: cannot find symbol
symbol  : class Baz
location: class Foo
  private Baz baz;
          ^
1 error

Это имеет смысл, если вы думаете об этом. Если Foo.java относится к Baz, но нет Baz.java(или Baz.class), как javac может узнать, в каком исходном файле искать?

Если вы вместо этого скажете javac для компиляции Foo.java и Bar.java одновременно, или даже если вы предварительно скомпилировали Bar.java(оставив Baz.class, где javac может его найти), эта ошибка исчезнет. Это делает ваш процесс сборки очень ненадежным и неровным.

Поскольку фактическое ограничение, которое больше похоже на "не относится к классу верхнего уровня из другого файла, если оно не имеет то же имя, что и файл, или вы также ссылаетесь на класс, который в том же файл, который назвал то же самое, что и файл," очень трудно следовать", люди обычно идут с гораздо более простым (хотя и более строгим) соглашением о простом размещении одного класса верхнего уровня в каждом файле. Это также лучше, если вы когда-нибудь передумали, должен ли класс быть открытым или нет.

Иногда действительно есть веская причина, почему каждый делает что-то определенным образом.

Ответ 3

Я полагаю, вы просто называете PrivateImpl тем, чем он является: non-public top-level class. Вы также можете объявить non-public top-level interfaces.

например, в других местах SO: непубличный класс верхнего уровня против статического вложенного класса

Что касается изменений в поведении между версиями, то было обсуждение о том, что "отлично работало" в 1.2.2. но перестала работать в 1.4 на форуме Sun: Java Compiler - не удалось объявить непубличные классы верхнего уровня в файле.

Ответ 4

У вас может быть столько классов, сколько хотите.

public class Fun {
    Fun() {
        System.out.println("Fun constructor");
    }
    void fun() {
        System.out.println("Fun mathod");
    }
    public static void main(String[] args) {
        Fun fu = new Fun();
        fu.fun();
        Fen fe = new Fen();
        fe.fen();
        Fin fi = new Fin();
        fi.fin();
        Fon fo = new Fon();
        fo.fon();
        Fan fa = new Fan();
        fa.fan();
        fa.run();
    }
}

class Fen {
    Fen() {
        System.out.println("fen construuctor");

    }
    void fen() {
        System.out.println("Fen method");
    }
}

class Fin {
    void fin() {
        System.out.println("Fin method");
    }
}

class Fon {
    void fon() {
        System.out.println("Fon method");
    } 
}

class Fan {
    void fan() {
        System.out.println("Fan method");
    }
    public void run() {
        System.out.println("run");
    }
}

Ответ 5

1.Есть ли здесь точное название для этой техники (аналогично внутреннему, вложенному, анонимному)?

Многоуровневая однофайловая демонстрация.

2. JLS говорит, что система может обеспечить ограничение, на которое эти вторичные классы не могут ссылаться кодом в других единицах компиляции пакета, например, они не могут рассматриваться как частные. Это действительно что-то, что меняется между реализациями Java?

Я не знаю ни одного, у которого нет этого ограничения, - все компиляторы на основе файлов не позволят вам ссылаться на классы исходного кода в файлах, которые не называются такими же, как имя класса. (если вы скомпилируете многоклассовый файл и поместите классы в путь класса, то любой компилятор найдет их)

Ответ 6

В соответствии с эффективной версией Java 2 (пункт 13):

"Если класс (или интерфейс) класса-частного класса используется только один класс, подумайте о том, чтобы сделать класс верхнего уровня частным вложенным классом единственного класса, который его использует (пункт 22). Это уменьшает доступность всех классов в пакете до одного класса который использует его. Но гораздо важнее уменьшить доступность бесплатного публичного класса, чем класс высшего класса высшего уровня:..."

Вложенный класс может быть статическим или нестатическим, исходя из того, нужен ли классу-члену доступ к охватывающему экземпляру (пункт 22).

Ответ 7

Да, вы можете, с открытыми статическими членами внешнего открытого класса, вот так:

public class Foo {

    public static class FooChild extends Z {
        String foo;
    }

    public static class ZeeChild extends Z {

    }

}

и другой файл, который ссылается на выше:

public class Bar {

    public static void main(String[] args){

        Foo.FooChild f = new Foo.FooChild();
        System.out.println(f);

    }
}

положить их в одну папку. Компилировать с:

javac folder/*.java

и запустить с:

 java -cp folder Bar

Ответ 8

Нет, ты не можешь Но это очень возможно в Scala:

class Foo {val bar = "a"}
class Bar {val foo = "b"}