【问题标题】:Java: Multiple class declarations in one fileJava:一个文件中的多个类声明
【发布时间】:2011-01-21 03:45:37
【问题描述】:

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

【问题讨论】:

  • +1 好问题。我从来没有真正考虑过这件事,因为几乎没有必要这样做。
  • 请注意,这是一个退化特征;如果 java 从一开始就有嵌套类,那将永远不可能。

标签: java class


【解决方案1】:

javac 并没有主动禁止这一点,但它确实有一个限制,这几乎意味着你永远不想从另一个文件中引用顶级类,除非它与它所在的文件具有相同的名称。

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

Foo.java 包含:

  • 公开课 Foo

Bar.java 包含:

  • 公共类吧
  • 类 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 可以找到的地方),或者即使 Foo.java碰巧除了 Baz 之外还引用了 Bar,然后这个错误就消失了。然而,这会让您的构建过程感觉非常不可靠和不稳定。

因为实际的限制,更像是“不要引用另一个文件中的顶级类,除非它与它所在的文件同名,或者您还引用了另一个名为与该文件中的那个文件相同的东西”有点难以遵循,人们通常会采用更直接(尽管更严格)的约定,即在每个文件中只放置一个顶级类。如果您改变了关于是否应该公开课程的想法,这也会更好。

在这种情况下,较新版本的 javac 也可以使用-Xlint:all 产生警告:

./Bar.java 中的辅助类 Baz 不应从其自己的源文件外部访问

有时每个人都以特定的方式做某事确实有充分的理由。

【讨论】:

  • Maven 做些什么来使编译可靠吗?
  • @Laurence 你说如果Foo.java引用了Baz,但是没有Baz.java(或者Baz.class),那么javac怎么知道要看什么源文件呢? i> 但即使我创建了 Baz.java,编译 Foo.java 也会失败。它仍然不知道要查看哪个源文件。
  • @KushalKumar javac 将搜索源路径,默认为用户类路径(又默认为当前目录)。如果您的源不是来自类路径中的目录,则需要指定源路径。
  • @LaurenceGonsalves 谢谢。读了几遍后,我可以很好地理解它。除了当你说实际限制是什么时,“不要引用另一个文件中的顶级类,除非它与它所在的文件同名,或者你也指的是一个类与文件名称相同的同一个文件”您能否也解释一下这一行?
  • @KushalKumar javac 从您特别要求它编译的一组源文件开始。当您引用不在这些源代码中的类时,它会检查已编译类的类路径,以及 具有匹配名称的源文件 的源路径。如果找到后者,则将其添加到正在处理的源文件集中。因此,如果您引用 Baz,并且它与 Bar 在同一个文件中,javac 只会找到 Baz,因为它由于对 Bar 的引用而拉入 Bar.java。
【解决方案2】:

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

至于这是否真的在实现之间发生变化 - 我非常怀疑它,但如果你一开始就避免这样做,你永远不需要关心:)

【讨论】:

  • 我不是反对者,但事实上这个答案可以被称为“规范”(即“你应该”而不是“事实上......但是......”)我认为这是最可能的原因。它实际上并没有回答任何问题。就像引发一个不相关的异常而不是返回任何东西/引发一个包含有关实际事实而不是意见的信息的异常。
  • 我发现我认为是 @JonSkeet 建议使用嵌套类型的一个小例外(否则我会同意):如果主类是泛型并且类型参数是第二个类,第二类不能嵌套。如果这两个类是紧密耦合的(比如问题中的 PublicClass 和 PrivateImpl),我认为将 PrivateImpl 作为顶级类放在同一个文件中是个好主意。
  • @BoomerRogers:不,这绝对不是“基于组件的编程的核心基础”。如果您正在针对组件进行编程,您为什么要关心源代码的组织方式? (我个人更喜欢依赖注入而不是服务定位器模式,但这是另一回事。)在你的脑海中分离API和源代码组织 - 它们是非常不同的东西。
  • @JonSkeet 让我改述一下:您的“答案”是个人无关的意见。 (即“混乱”和“我怀疑”之类的答案没有什么价值。)所以,你的帖子没有回答任何两个提出的问题。检查 polygenelubricants 的答案,你会发现他设法回答了这两个问题。
  • @bvdb: (并且有 很多 的事情是不好的做法,但规范允许。我敦促人们也不要写 public int[] foo(int x)[] { return new int[5][5]; },即使这是有效的。)
【解决方案3】:

我相信您只需调用PrivateImpl 它是什么:non-public top-level class。你也可以声明non-public top-level interfaces

例如,SO 上的其他地方:Non-public top-level class vs static nested class

至于版本之间的行为变化,有关于 1.2.2 中“完美运行”的内容的讨论。但在 sun 的论坛中停止了 1.4 的工作:Java Compiler - unable to declare a non public top level classes in a file

【讨论】:

  • 我唯一的问题是你可以让 non-public top level class 成为文件中唯一的类,因此它不能解决多重性问题。
  • 我理解这种担忧,但正如您所见,这是其他人历来使用的术语。如果我必须自己编一个术语,我可能会称之为secondary top level types
  • 这确实是一个仅链接的答案,现在到太阳论坛的链接不起作用,剩下的不多了
【解决方案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");
    }
}

【讨论】:

  • @Nenotlep 当您执行“改进格式”时,请注意不要与代码本身混淆,例如删除反斜杠。
  • 这没有回答问题。
  • 这是最适合我的。
【解决方案5】:

1.这种技术有一个整洁的名字吗(类似于inner、nested、anonymous)?

多类单文件演示。

2. JLS 表示系统可能会强制限制这些次要类不能被包的其他编译单元中的代码引用,例如,它们不能被视为包私有。这真的会在 Java 实现之间发生变化吗?

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

【讨论】:

    【解决方案6】:

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

    【讨论】:

      【解决方案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】:

      根据 Effective Java 第 2 版(第 13 条):

      "如果一个包私有的顶级类(或接口)只被 一个类,考虑将顶级类设为私有嵌套类 使用它的唯一类(第 22 项)。这减少了它的 从其包中的所有类到一个类的可访问性 使用它。但更重要的是减少可访问性 一个免费的公共类而不是一个包私有的顶级类: ……”

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

      【讨论】:

      • OP 没有询问嵌套类。
      【解决方案9】:

      没有。你不能。但这在 Scala 中是很有可能的:

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

      【讨论】:

        猜你喜欢
        • 2018-03-26
        • 1970-01-01
        • 2015-06-25
        • 1970-01-01
        • 2013-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多