Java:1つのファイル内の複数のクラス宣言


238

Javaでは、1つのファイルで複数の最上位クラスを定義できます。ただし、これらのクラスの最大1つがパブリックであることが条件です(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良い質問です。これを行う必要はほとんどないので、私は本当に問題について多くのことを考えたことはありません。
マイケルマイヤーズ

12
これは痕跡的な機能であることに注意してください。Javaが最初からクラスをネストしていたとしたら、それは決して不可能でした。
Kevin Bourrillion、2010

回答:


120

この手法(私の単一のソースファイルに複数の最上位クラスを含む)に推奨される名前は「混乱」になります。真剣に、私はそれが良い考えではないと思います-私は代わりにこの状況ではネストされたタイプを使用します。それでも、どのソースファイルが含まれているかを予測するのは簡単です。ただし、このアプローチには正式な用語があるとは思いません。

これが実際に実装間で変更されるかどうかについては、私はそれを非常に疑っていますが、最初からそれを行わない場合は、気にする必要はありません:)


71
私は反対投票者ではありませんが、この回答が「規範的」と呼ばれるものであるという事実(つまり、「実際には...しかし...ではなく、「すべきです」)は、私は思う反対投票を取得します。実際にはどの質問にも答えません。何かを返すのではなく無関係な例外を発生させる/意見の代わりに実際の事実に関する情報を持つ例外を発生させるようなものです。
n611x007

6
ネストされたタイプを使用するという@JonSkeetの提案(そうでなければ同意します)に対するマイナーな例外だと思ったものを見つけました:メインクラスがジェネリックでタイプパラメーターが2番目のクラスである場合、2番目のクラスはできません入れ子にします。2つのクラスが密接に結合されている場合(問題のPublicClassとPrivateImplなど)、PrivateImplを同じファイルのトップレベルのクラスとして配置することをお勧めします。
jfritz42

6
@BoomerRogers:いいえ、これは間違いなく「コンポーネントベースのプログラミングの中核」ではありません。コンポーネントに対してプログラミングをしている場合、なぜソースコードがどのように編成されているかを気にするのでしょうか。(個人的には、サービスロケーターパターンよりも依存性注入を好みますが、それは別の問題です。)心の中でAPIとソースコードの構成を分離する-それらは非常に異なります。
Jon Skeet、2014年

1
@JonSkeet言い換えさせてください:あなたの「答え」は個人的な無関係な意見です。(つまり、「混乱」や「疑わしい」などの回答はほとんど意味がありません。)したがって、投稿は2つの質問のいずれにも回答しません。polygenelubricantsの答えを確認すると、彼が両方に答えることができます。
bvdb 2015

1
@bvdb:(そして、悪い習慣ですが、仕様で許可されているものはたくさんありpublic int[] foo(int x)[] { return new int[5][5]; }ます。それが有効であるとしても、私も書かないように強くお勧めします。)
Jon Skeet

130

javacはこれを積極的に禁止していませんが、それが含まれているファイルと同じ名前を持たない限り、別のファイルからトップレベルクラスを参照したくないというほとんどの制限があります。

Foo.javaとBar.javaの2つのファイルがあるとします。

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をコンパイルした場合(javacがそれを見つけることができる場所にBaz.classを残す)でも、このエラーは発生しません。ただし、これによりビルドプロセスの信頼性が低下し、不安定な感じになります。

実際の制限は、「それが含まれているファイルと同じ名前であるか、同じ名前のファイル内にあるクラスを参照しているのでない限り、別のファイルの最上位クラスを参照しないでください。 「ファイルと同じもの」を理解するのはちょっと難しいです。通常、人々は各ファイルにトップレベルのクラスを1つだけ置くというはるかに単純な(より厳密ではありますが)慣例に従います。これは、クラスを公開するかどうかについて考えを変える場合にも適しています。

時々、誰もが特定の方法で何かをするのに本当に正当な理由があります。


Mavenはコンパイルを信頼できるものにするために何かしますか?
Aleksandr Dubinsky

23

私はあなたが単にPrivateImplそれを何と呼ぶかを信じています:a non-public top-level class。宣言もできnon-public top-level interfacesます。

例:SOの他の場所:非公開トップレベルクラスと静的ネストクラス

バージョン間の動作の変更に関しては、1.2.2で「完全に機能する」ものについてこの議論がありました。しかし、Sunのフォーラムの1.4で動作しなくなりました。Javaコンパイラ-非公開の最上位クラスをファイルで宣言できません


1
これに関する私の唯一の問題non-public top level classは、ファイルでクラスを唯一のクラスにすることができるため、多重度に対応できないことです。
Michael Brewer-Davis、

私は懸念を理解していますが、ご覧のとおり、これは他の人が歴史的に使用している用語です。私自身の言葉を作り上げる必要があるなら、私はそれをおそらく呼ぶでしょう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
それは質問に答えません。
2017

4

1.この手法に整然とした名前はありますか(内部、入れ子、匿名に類似)?

マルチクラスの単一ファイルのデモ。

2.JLSは、システムがこれらのセカンダリクラスをパッケージの他のコンパイルユニットのコードから参照できないという制限を強制する可能性があると述べています。たとえば、それらはパッケージプライベートとして処理できません。それは本当にJava実装間で変わるものですか?

私はその制限がないものは知りません-すべてのファイルベースのコンパイラは、クラス名と同じ名前が付けられていないファイル内のソースコードクラスを参照することを許可しません。(マルチクラスファイルをコンパイルし、クラスパスにクラスを配置すると、コンパイラはそれらを見つけます)


1

Effective Java 2nd Edition(Item 13)によると:

「パッケージプライベートトップレベルクラス(またはインターフェイス)が1つのクラスのみで使用される場合、トップレベルクラスを、それを使用する唯一のクラスのネストされたプライベートクラスにすることを検討してください(アイテム22)。パッケージ内のクラスを、それを使用する1つのクラスに変更します。ただし、パッケージプライベートトップレベルクラスよりも、不必要にパブリッククラスのアクセシビリティを減らす方がはるかに重要です。

ネストされたクラスは、メンバークラスが囲んでいるインスタンス(アイテム22)にアクセスする必要があるかどうかに基づいて、静的または非静的にできます。


OPはネストされたクラスについて尋ねていません。
charmoniumQ 2018

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

その例は質問に答えません。ネストされた静的クラスの例を示していますが、これは、2つの最上位クラスが同じファイルで定義されているのと同じではありません。
ペドロガルシアメディナ

0

参考までに、Java 11以降を使用している場合、このルールには例外があります。Javaファイルを直接(コンパイルせずに)実行する場合です。このモードでは、ファイルごとに1つのパブリッククラスに制限はありません。ただし、mainメソッドを含むクラスは、ファイルの最初のクラスでなければなりません。


-5

いいえ、できません。しかし、Scalaではそれが非常に可能です。

class Foo {val bar = "a"}
class Bar {val foo = "b"}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.