Java : 하나의 파일에 여러 클래스 선언


237

Java에서는 단일 파일에서 여러 최상위 레벨 클래스를 정의 할 수 있으며,이 중 최대 하나가 공개되어 있습니다 ( JLS §7.6 참조 ). 예를 들어 아래를 참조하십시오.

  1. 이 기술에 대한 단정 한 이름 (유사 있는가 inner, nested, anonymous)?

  2. JLS에 따르면 시스템 이러한 2 차 클래스가 referred to by code in other compilation units of the package패키지 개인 클래스 로 취급 될 수없는 이러한 2 차 클래스를 제한 할 수 있다고 제한합니다 . 실제로 Java 구현간에 변경되는 것이 있습니까?

예를 들어 PublicClass.java :

package com.example.multiple;

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

class PrivateImpl {
    int implementationData;
}

11
+1 좋은 질문입니다. 나는 이것을 할 필요가 거의 없기 때문에 실제로 많은 생각을하지 않았습니다.
Michael Myers

12
이것은 흔적 기능입니다. 자바가 처음부터 중첩 클래스를 가지고 있었다면 불가능했을 것입니다.
Kevin Bourrillion

답변:


120

이 기술의 제안 된 이름 (단일 소스 파일에 여러 최상위 클래스 포함)은 "mess"입니다. 진지하게, 나는 그것이 좋은 생각이라고 생각하지 않습니다-대신이 상황에서 중첩 유형을 사용합니다. 그러면 어떤 소스 파일이 들어 있는지 쉽게 예측할 수 있습니다.이 방법에 대한 공식 용어는 없다고 생각합니다.

이것이 실제로 구현간에 변경되는지 여부에 대해서는 의심 스럽지만 처음부터 수행하지 않으면 걱정할 필요가 없습니다. :)


71
나는 downvoter는 아니지만이 답변은 "규범 적"이라고 할 수있는 것입니다 (즉, "실제로 ...하지만 ..."대신 "당신은"해야합니다). 내가 생각하는 공감대를 얻을. 실제로 어떤 질문에도 대답하지 않습니다. 의견 대신 실제 사실에 대한 정보가있는 예외를 발생시키는 대신 무관 한 예외를 제기하는 것과 같습니다.
n611x007

6
@JonSkeet의 중첩 유형을 사용하라는 제안에 약간의 예외가 있다고 생각합니다. 중첩됩니다. 그리고 두 클래스가 밀접하게 결합되어 있다면 (문제의 PublicClass 및 PrivateImpl과 같이) PrivateImpl을 동일한 파일의 최상위 클래스로 두는 것이 좋습니다.
jfritz42

6
@BoomerRogers : 아닙니다. 이것은 "컴포넌트 기반 프로그래밍의 핵심 기반" 이 아닙니다 . 컴포넌트에 대해 프로그래밍하는 경우 소스 코드의 구성 방식에 관심이있는 이유는 무엇입니까? (개인적으로 서비스 로케이터 패턴보다는 의존성 주입을 선호 하지만 다른 문제입니다.) 별도의 API와 소스 코드 구성을 생각하면 매우 다릅니다.
Jon Skeet

1
@JonSkeet 다음과 같이 다시 말해 보겠습니다. "답변"은 개인적인 관련이없는 의견입니다. (예 : '메스'와 '답변'과 같은 답은 가치가 거의 없습니다.) 따라서 귀하의 게시물은 두 가지 제기 된 질문 중 어느 것에도 답변하지 않습니다. polygenelubricants의 답을 확인하면 그가 두 가지 모두에 답할 수 있음을 알 수 있습니다.
bvdb

1
@bvdb : (그리고 나쁜 습관이지만 사양에 의해 허용되는 것들이 많이 있습니다. 그것이 public int[] foo(int x)[] { return new int[5][5]; }유효하더라도 사람들이 글을 쓰지 말라고 권합니다 .)
Jon Skeet

130

javac는 이것을 적극적으로 금지하지 않지만, 파일과 같은 이름을 갖지 않으면 다른 파일에서 최상위 클래스를 참조하고 싶지 않다는 한계가 있습니다.

Foo.java와 Bar.java라는 두 파일이 있다고 가정하십시오.

Foo.java는 다음을 포함합니다 :

  • 공공 클래스 푸

Bar.java는 다음을 포함합니다.

  • 공공 클래스 바
  • 학급

또한 모든 클래스가 동일한 패키지에 있고 파일이 같은 디렉토리에 있다고 가정 해 봅시다.

Foo.java가 Baz가 아닌 Barz를 참조하고 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를 남겨둔 경우)이 오류는 사라집니다. 그러나 이것은 빌드 프로세스를 매우 신뢰할 수없고 색다른 느낌으로 만듭니다.

실제 제한은 "파일과 이름이 같거나 이름이 같은 파일에있는 클래스를 참조하지 않는 한 다른 파일의 최상위 클래스를 참조하지 않습니다. 파일과 같은 것은 "따라하기가 쉽지 않습니다. 사람들은 일반적으로 각 파일에 하나의 최상위 클래스를 배치하는 훨씬 더 직접적인 (그러나 더 엄격한) 규칙을 따릅니다. 수업이 공개되어야하는지 아닌지에 대해 마음이 바뀌면 더 좋습니다.

때때로 모든 사람이 특정한 방식으로 무언가를하는 좋은 이유가 있습니다.


Maven은 컴파일을 안정적으로 만들기 위해 어떤 작업을 수행합니까?
Aleksandr Dubinsky

23

나는 당신이 단순히 PrivateImpl그것을 무엇 이라고 부르는지 믿습니다 non-public top-level class. 선언 할 수도 non-public top-level interfaces있습니다.

예를 들어 SO의 다른 곳 : 비공개 최상위 클래스 대 정적 중첩 클래스

버전 간 동작 변경에 대해서는 1.2.2에서 "완벽하게 작동 한"항목에 ​​대한 논의가있었습니다. Java 포럼 에서 파일에서 비 공용 최상위 레벨 클래스를 선언 할 수 없습니다 .


1
이것에 대한 유일한 문제 non-public top level class는 파일에서 유일한 클래스 가 될 수 있으므로 다중성을 다루지 않는다는 것입니다.
Michael Brewer-Davis

나는 우려를 이해하지만, 당신이 볼 수 있듯이 이것은 다른 사람들이 역사적으로 사용한 용어입니다. 나만의 용어를 만들어야한다면 아마 전화 할 것 secondary top level types입니다.
polygenelubricants

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 "포맷 향상"을 할 때는 백 슬래시 제거와 같이 코드 자체가 엉망이되지 않도록주의하십시오.
Tom

3
그것은 질문에 대답하지 않습니다.
ᴠɪɴᴄᴇɴᴛ

4

1.이 기술에 대한 확실한 이름이 있습니까 (내부, 내포, 익명과 유사)?

멀티 클래스 단일 파일 데모.

JLS는 시스템이 패키지의 다른 컴파일 단위에서 코드로 이러한 보조 클래스를 참조 할 수 없다는 제한을 적용 할 수 있다고 말합니다 (예 : 패키지 전용으로 처리 할 수 ​​없음). 실제로 Java 구현간에 변경되는 것이 있습니까?

나는 그 제한이없는 것을 알지 못합니다-모든 파일 기반 컴파일러는 클래스 이름과 동일하지 않은 파일의 소스 코드 클래스를 참조 할 수 없습니다. (멀티 클래스 파일을 컴파일하고 클래스 경로에 클래스를 넣으면 모든 컴파일러에서 찾을 수 있습니다)


1

Effective Java 2nd edition (Item 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

이 예는 질문에 대답하지 않습니다. 중첩 된 정적 클래스의 예를 제공하는데, 동일한 파일에 두 개의 최상위 클래스가 정의되어있는 것과 다릅니다.
Pedro García Medina

0

참고로 Java 11 이상을 사용하는 경우 Java 파일을 직접 실행하는 경우 ( 컴파일없이 ) 이 규칙에는 예외가 있습니다. 이 모드에서는 파일 당 단일 공용 클래스에 대한 제한이 없습니다. 그러나 main메소드가있는 클래스 는 파일에서 첫 번째 클래스 여야합니다.


-5

아뇨. 못해요 그러나 스칼라에서는 매우 가능합니다.

class Foo {val bar = "a"}
class Bar {val foo = "b"}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.