Java:一个文件中包含多个类声明


238

在Java中,您可以在一个文件中定义多个顶级类,条件是其中最多一个是公共的(请参见JLS§7.6)。参见以下示例。

  1. 是否有此技术整洁名(类似于innernestedanonymous)?

  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;
}

11
+1好问题。我从来没有真正考虑过这个问题,因为几乎没有必要这样做。
Michael Myers

12
请注意,这是一种残留特征;如果java从一开始就嵌套了类,那将永远不可能。
凯文·布兰里恩

Answers:


120

对于这种技术,我建议的名称(在一个源文件中包括多个顶级类)将是“ mess”。认真地说,我认为这不是一个好主意-在这种情况下,我将使用嵌套类型。然后,仍然很容易预测该文件位于哪个源文件中。我不相信这种方法有官方术语。

至于这是否真的在实现之间发生了变化-我对此表示高度怀疑,但是如果您一开始就避免这样做,则无需担心:)


71
我不是拒绝投票的人,但是这个答案可以被称为“规范性”(即“您应该”而不是“实际上……但是……”),这是最可能的原因我觉得很不高兴。它实际上并没有回答任何问题。就像提出不相关的异常而不是返回任何东西一样/提出具有关于实际事实的信息而不是观点的异常。
n611x007

6
我发现我认为@JonSkeet使用嵌套类型的建议是一个次要例外(否则我会同意):如果主类是泛型并且type参数是第二个类,则第二个类不能被嵌套。而且,如果两个类紧密耦合(如问题中的PublicClass和PrivateImpl),我认为将PrivateImpl作为同一文件中的顶级类是一个好主意。
jfritz42

6
@BoomerRogers:不,这绝对不是 “基于组件编程的核心基础”。如果要针对组件进行编程,为什么还要关心源代码的组织方式?(个人而言,我更喜欢依赖注入而不是服务定位器模式,但这是另一回事。)在您看来,API和源代码的组织是分开的-它们是完全不同的东西。
乔恩·斯基特

1
@JonSkeet我再说一遍:您的“答案”是与个人无关的意见。(即“混乱”和“我怀疑”之类的答案几乎没有价值。)因此,您的帖子没有回答这2个提出的问题中的任何一个。检查多基因润滑剂的答案,您会发现他设法同时回答了这两种情况。
bvdb 2015年

1
@bvdb :(还有很多不好的做法,但规范允许这样做。我敦促人们也不要写public int[] foo(int x)[] { return new int[5][5]; },即使那是有效的。)
Jon Skeet 2015年

130

javac并没有积极禁止这样做,但是它确实有一个限制,那就是几乎意味着您永远都不想引用另一个文件中的顶级类,除非它的名称与所在文件的名称相同。

假设您有两个文件Foo.java和Bar.java。

Foo.java包含:

  • 公共课

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可以找到它的位置),那么该错误就会消失。但是,这会使您的构建过程变得非常不可靠且不稳定。

因为实际的限制(更像是“不要引用另一个文件中的顶级类,除非它与所在文件具有相同的名称,否则您也要引用该文件中的同一个类被命名为与文件相同”很难遵循,人们通常会采用更为直接(尽管更严格)的约定,即在每个文件中只放置一个顶级类。如果您改变了对某个班级是否公开的想法,这也更好。

有时候,确实有充分的理由说明为什么每个人都以特定的方式做某事。


Maven是否做任何使编译可靠的事情?
Aleksandr Dubinsky

23

我相信您只是称呼PrivateImpl它为:a non-public top-level class。您也可以声明non-public top-level interfaces

例如,SO上的其他地方:非公共顶级类与静态嵌套类

至于版本之间的行为变化,这里讨论的是在1.2.2中“完美工作”的东西。但是在sun的论坛:Java Compiler中停止在1.4中工作-无法在文件中声明非公共顶级类


1
我唯一的问题是您可以non-public top level class在文件中拥有唯一的类,因此它不能解决多重性问题。
Michael Brewer-Davis 2010年

我了解您的担忧,但是正如您所看到的,这是其他人历来使用的术语。如果我必须弥补自己的任期,我​​可能会称之为secondary top level types
polygenelubricants 2010年

7

您可以根据需要选择任意多个班级

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");
    }
}

1
@Nenotlep当您进行“改进格式设置”时,还请注意它不会与代码本身混淆,例如删除反斜杠。
汤姆

3
那没有回答问题。
ᴠɪɴᴄᴇɴᴛ

4

1.该技术是否有一个简洁的名称(类似于内部,嵌套,匿名)?

多类单文件演示。

2. JLS表示,系统可能会强制执行以下限制:这些二级类不能被程序包其他编译单元中的代码引用,例如,它们不能被视为程序包私有。这真的在Java实现之间有所改变吗?

我不知道没有任何限制-所有基于文件的编译器都不允许您引用名称与类名不同的文件中的源代码类。(如果您编译了一个多类文件,并将这些类放在类路径中,那么任何编译器都将找到它们)


1

根据有效的Java第二版(第13项):

“如果只有一个类使用包私有的顶级类(或接口),请考虑使顶级类成为使用它的唯一类的私有嵌套类(第22项)。这降低了所有人的可访问性将包中的类转换为使用它的一个类。但是,减少免费公共类的可访问性比打包私有顶级类要重要得多:...”

根据成员类是否需要访问封闭实例(项目22),嵌套类可以是静态的也可以是非静态的。


OP并未询问嵌套类。
charmoniumQ

0

是的,您可以在外部公共类中使用公共静态成员,如下所示:

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

该示例未回答问题。您将给出一个嵌套的静态类的示例,它与在同一文件中定义两个顶级类不同。
佩德罗·加西亚梅迪纳

0

仅供参考,如果您使用的是Java 11+,则此规则有一个例外:如果直接运行Java文件(无需编译)。在这种模式下,每个文件没有一个公共类的限制。但是,带有该main方法的类必须是文件中的第一个类。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.