Java: несколько объявлений классов в одном файле - PullRequest
221 голосов
/ 25 февраля 2010

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

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

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

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

package com.example.multiple;

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

class PrivateImpl {
    int implementationData;
}

Ответы [ 8 ]

122 голосов
/ 25 февраля 2010

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

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

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

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

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

  • Бар общественного класса
  • класс Баз

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

Что произойдет, если 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 может его найти), то эта ошибка исчезнет. Однако это делает процесс сборки очень ненадежным и ненадежным.

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

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

119 голосов
/ 25 февраля 2010

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

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

22 голосов
/ 25 февраля 2010

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

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

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

6 голосов
/ 19 августа 2015

Вы можете иметь столько классов, сколько пожелаете

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");
    }
}
4 голосов
/ 25 февраля 2010

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

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

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

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

1 голос
/ 16 ноября 2018

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

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
1 голос
/ 19 ноября 2016

Согласно Effective Java, 2-е издание (пункт 13):

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

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

0 голосов
/ 09 января 2019

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

class Foo {val bar = "a"}
class Bar {val foo = "b"}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...