Java: несколько объявлений классов в одном файле
В Java вы можете определить несколько классов верхнего уровня в одном файле, при условии, что не более одного из них является общедоступным (см. JLS §7.6). Смотрите ниже, например.
Есть ли аккуратное название для этой техники (аналогично
inner
,nested
,anonymous
)?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 ответов
Мое предлагаемое название для этого метода (включая несколько классов верхнего уровня в одном исходном файле) будет "беспорядок". Серьезно, я не думаю, что это хорошая идея - вместо этого я бы использовал вложенный тип в этой ситуации. Тогда все еще легко предсказать, в каком исходном файле он находится. Хотя я не верю, что для этого подхода есть официальный термин.
Что касается того, меняется ли это на самом деле между реализациями - я сильно сомневаюсь в этом, но если вы избегаете делать это в первую очередь, вам никогда не нужно будет заботиться:)
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 может его найти), то эта ошибка исчезнет. Это делает ваш процесс сборки очень ненадежным и нестабильным.
Поскольку фактическое ограничение, которое больше похоже на "не ссылается на класс верхнего уровня из другого файла, если только у него нет того же имени, что и у файла, в котором он находится, или вы также ссылаетесь на класс, который находится в том же файле с именем то же самое, что и с файлом, довольно трудно понять, люди обычно придерживаются гораздо более простого (хотя и более строгого) соглашения о том, чтобы просто поместить один класс верхнего уровня в каждый файл. Это также лучше, если вы когда-нибудь передумаете, должен ли класс быть публичным или нет.
Иногда действительно есть веская причина, почему все делают что-то определенным образом.
Я полагаю, вы просто позвоните PrivateImpl
что это такое: non-public top-level class
, Вы также можете объявить non-public top-level interfaces
также.
например, в других местах SO: непубличный класс верхнего уровня против статического вложенного класса
Что касается изменений в поведении между версиями, то было обсуждение о том, что "отлично работало" в 1.2.2. но перестал работать в 1.4 на форуме Sun. Java Compiler - не удалось объявить непубличные классы верхнего уровня в файле.
Вы можете иметь столько классов, сколько пожелаете
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");
}
}
К вашему сведению, если вы используете Java 11+, есть исключение из этого правила: если вы запускаете свой java-файл напрямую (без компиляции). В этом режиме нет ограничений на один открытый класс для каждого файла. Однако класс сmain
метод должен быть первым в файле.
1. Есть ли аккуратное название для этой техники (аналог внутренней, вложенной, анонимной)?
Мультиклассовая демонстрация одного файла.
2. JLS говорит, что система может применять ограничение, согласно которому эти вторичные классы не могут ссылаться кодом в других единицах компиляции пакета, например, они не могут рассматриваться как закрытые для пакета. Это действительно то, что меняется между реализациями Java?
Я не знаю ни одного, у кого нет этого ограничения - все файловые компиляторы не позволят вам ссылаться на классы исходного кода в файлах, имена которых не совпадают с именами классов. (если вы скомпилируете файл с несколькими классами и поместите классы в путь к классам, тогда любой компилятор найдет их)
Да, вы можете, с открытыми статическими членами внешнего открытого класса, вот так:
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
Согласно изданию Effective Java 2 (пункт 13):
"Если приватный класс верхнего уровня (или интерфейс) пакета используется только одним классом, рассмотрите возможность сделать класс верхнего уровня частным вложенным классом единственного класса, который его использует (пункт 22). Это уменьшает его доступность для всех классы в его пакете до одного класса, который его использует. Но гораздо важнее уменьшить доступ к безвозмездно общедоступному классу, чем к классу верхнего уровня частного пакета: ... "
Вложенный класс может быть статическим или нестатическим в зависимости от того, нужен ли классу-члену доступ к включающему экземпляру (элемент 22).
Нет, ты не можешь Но это очень возможно в Scala:
class Foo {val bar = "a"}
class Bar {val foo = "b"}