자바 : 서브 패키지 가시성?


150

나는 내 프로젝트에서 두 패키지를 가지고 odp.projodp.proj.test. 이 두 패키지의 클래스에만 표시하려는 특정 메소드가 있습니다. 어떻게해야합니까?

편집 : Java에 하위 패키지 개념이 없으면이 방법이 있습니까? 테스터 및 해당 패키지의 다른 멤버에게만 제공되는 특정 방법이 있습니다. 모든 것을 동일한 패키지에 넣어야합니까? 광범위한 반사를 사용하십니까?




2
또한 테스트는 패키지 외부에서 관찰 가능한 객체 의 동작 만 테스트해야 합니다. 테스트에서 패키지 범위 메서드 / 클래스에 액세스하면 테스트가 동작이 아닌 구현을 테스트하고 있음을 알 수 있습니다. maven 또는 gradle과 같은 빌드 도구를 사용하면 동일한 클래스 경로에서 테스트를 쉽게 실행할 수 있지만 최종 jar에는 포함되지 않으므로 다른 패키지 이름을 가질 필요가 없습니다. 어쨌든 그것들을 별도의 패키지에 넣는 것은 개인 / 기본 범위에 액세스하지 않고 공개 API 만 테스트 하도록 강제하는 것 입니다.
derekv

3
순전히 행동 ​​중심 방식으로 작업하고 테스트에서 블랙 박스 테스트 만 수행하려는 경우에 해당됩니다. 그러나 원하는 행동을 구현하기 위해서는 불가피하게 높은 순환 복잡성이 요구되는 경우가있을 수 있습니다. 이 경우 구현을 더 작고 간단한 청크로 나누고 (여전히 구현 전용) 일부 단위 테스트를 작성하여 이러한 청크를 통해 다른 경로에서 화이트 박스 테스트를 수행하는 것이 좋습니다.
제임스 우즈

답변:


165

당신은 할 수 없습니다. Java에는 하위 패키지의 개념이 없으므로odp.projodp.proj.test완전히 분리 된 패키지입니다.


10
이 방법이 마음에 들지만 대부분의 IDE가 동일한 이름의 패키지를 함께 사용한다는 것은 혼란 스럽습니다. 설명해 주셔서 감사합니다.
JacksOnF1

이것은 엄격하게 정확하지 않습니다 다음 JLS는 불구하고, 서브 패키지를 정의하지 않습니다 단지 그들이 가지고있는 언어의 의미는 "최고 수준의 유형과 같은 간단한 이름의 서브 패키지를 갖는 패키지에 대해"금지하는 것입니다. 나는 이것을 자세히 설명하는이 질문에 대한 답변을 추가했습니다.
M. Justin

59

패키지 이름은 여기에서 응용 프로그램이 단위 테스트 용임을 암시합니다. 일반적으로 사용되는 패턴은 테스트하려는 클래스와 단위 테스트 코드를 동일한 패키지 (경우에 따라 odp.proj)이지만 다른 소스 트리에 배치하는 것입니다. 따라서 클래스를 넣고 src/odp/proj테스트 코드를에 넣으십시오 test/odp/proj.

Java에는 "패키지"액세스 수정자가 있습니다.이 수정자는 지정되지 않은 경우 기본 액세스 수정 자입니다 (즉, 공개, 개인 또는 보호를 지정하지 않음). "패키지"액세스 수정자를 사용하면 클래스의 클래스 만 odp.proj메소드에 액세스 할 수 있습니다. 그러나 Java에서는 액세스 수정자가 모든 액세스가 가능하므로 액세스 규칙을 적용하는 데 의존 할 수 없음을 명심하십시오. 제한적 보안 관리자가없는 경우 액세스 수정자는 단지 암시 적입니다.


11

이 사이에 특별한 관계 없다 odp.proj하고 odp.proj.test- 그들은 단지 분명히 관련라는 이름으로 일이.

odp.proj.test 패키지가 단순히 테스트를 제공하는 경우 동일한 패키지 이름 ( odp.proj)을 사용할 수 있습니다 . Eclipse 및 Netbeans와 같은 IDE는 패키지 이름은 동일하지만 JUnit 시맨틱 을 사용하여 별도의 폴더 ( src/main/java/odp/projsrc/test/java/odp/proj)를 작성합니다.

이 IDE는 메소드에 대한 테스트를 odp.proj생성하고 존재하지 않는 테스트 메소드에 대한 적절한 폴더를 작성합니다.


5

IntelliJ 에서이 작업을 수행하면 소스 트리는 다음과 같습니다.

src         // source root
- odp
   - proj   // .java source here
- test      // test root
  - odp
     - proj // JUnit or TestNG source here

4

편집 : Java에 하위 패키지 개념이 없으면이 방법이 있습니까? 테스터 및 해당 패키지의 다른 멤버에게만 제공되는 특정 방법이 있습니다.

아마도 그것들을 표시하지 않는 동기에 약간 의존하지만 유일한 이유는 테스트를위한 것 (또는 다른 내부적 인 것)으로 공용 인터페이스를 오염시키고 싶지 않다면 메소드를 별도의 공용 인터페이스를 사용하고 "숨겨진"메서드 소비자가 해당 인터페이스를 사용하도록합니다. 다른 사람들이 인터페이스를 사용하는 것을 막지는 않지만 당신이 그럴 이유가 없습니다.

단위 테스트의 경우, 로트를 다시 쓰지 않고 가능하다면 제안에 따라 동일한 패키지를 사용하십시오.


3

다른 사람들이 설명했듯이 Java에는 "하위 패키지"와 같은 것이 없습니다. 모든 패키지가 격리되어 부모로부터 아무것도 상속받지 않습니다.

다른 패키지에서 보호 된 클래스 멤버에 액세스하는 쉬운 방법은 클래스를 확장하고 멤버를 재정의하는 것입니다.

예를 들어 ClassInA패키지 에서 액세스하려면 a.b:

package a;

public class ClassInA{
    private final String data;

    public ClassInA(String data){ this.data = data; }

    public String getData(){ return data; }

    protected byte[] getDataAsBytes(){ return data.getBytes(); }

    protected char[] getDataAsChars(){ return data.toCharArray(); }
}

해당 패키지에서 필요한 메소드를 대체하는 클래스를 작성하십시오 ClassInA.

package a.b;

import a.ClassInA;

public class ClassInAInB extends ClassInA{
    ClassInAInB(String data){ super(data); }

    @Override
    protected byte[] getDataAsBytes(){ return super.getDataAsBytes(); }
}

그러면 다른 패키지의 클래스 대신 재정의 클래스를 사용할 수 있습니다.

package a.b;

import java.util.Arrays;

import a.ClassInA;

public class Driver{
    public static void main(String[] args){
        ClassInA classInA = new ClassInA("string");
        System.out.println(classInA.getData());
        // Will fail: getDataAsBytes() has protected access in a.ClassInA
        System.out.println(Arrays.toString(classInA.getDataAsBytes()));

        ClassInAInB classInAInB = new ClassInAInB("string");
        System.out.println(classInAInB.getData());
        // Works: getDataAsBytes() is now accessible
        System.out.println(Arrays.toString(classInAInB.getDataAsBytes()));
    }
}

이것은 확장 된 클래스 (상속)에 보이는 보호 된 멤버에 대해서만 작동하며 동일한 패키지 내의 하위 / 확장 클래스에만 보이는 패키지 전용 멤버에는 적용되지 않습니다. 잘만되면 이것은 누군가를 돕는다!


3

여기에있는 대부분의 답변에 따르면 Java에는 하위 패키지와 같은 것은 없지만 정확히 정확하지는 않습니다. 이 용어는 Java 6에 이르기까지 Java 언어 사양에 있었으며 아마도 더 뒤로 돌아 왔습니다 (이전 버전의 Java에 대해서는 자유롭게 액세스 할 수있는 JLS 버전이 아닌 것 같습니다). Java 6부터 JLS에서 하위 패키지 관련 언어가 크게 변경되지 않았습니다.

자바 13 JLS :

패키지의 멤버는 하위 패키지와 패키지의 모든 컴파일 단위에 선언 된 모든 최상위 클래스 유형 및 최상위 인터페이스 유형입니다.

예를 들어, Java SE Platform API에서 :

  • 이 패키지에는 java하위 패키지가 awt, applet, io, lang, net,와 util,하지만 컴파일 단위를.
  • 이 패키지 java.awt에는 image클래스 및 인터페이스 유형의 선언을 포함하는 여러 컴파일 단위뿐만 아니라 라는 하위 패키지 가 있습니다.

서브 패키지 개념은 패키지와 클래스 / 인터페이스 간의 이름 지정 제약 조건을 적용하는 것과 관련이 있습니다.

패키지에는 동일한 이름의 두 멤버가 포함되어 있지 않거나 컴파일 타임 오류가 발생할 수 있습니다.

여기 몇 가지 예가 있어요.

  • 패키지 java.awt에는 서브 패키지 가 있으므로 image, 클래스 또는 인터페이스 유형의 선언을 포함 할 수 없으며 포함하지도 않습니다 image.
  • 해당 패키지에 이름이 지정된 패키지 mouse와 멤버 유형 Button(이후이라고도 함 mouse.Button)이 있으면 정규화 된 이름 mouse.Button또는을 가진 패키지가있을 수 없습니다 mouse.Button.Click.
  • 경우 com.nighthacks.java.jag유형의 완전한 이름은 다음 누구의 정규화 된 이름 중 어떤 패키지가있을 수 없다 com.nighthacks.java.jag거나 com.nighthacks.java.jag.scrabble.

그러나이 이름 제한은 언어별로 하위 패키지에 제공 되는 유일한 의미입니다.

패키지의 계층 적 이름 지정 구조는 일반적인 방식으로 관련 패키지를 구성하는 데 편리하도록 만들어졌지만 해당 패키지에 선언 된 최상위 유형과 이름이 같은 하위 패키지를 갖는 패키지에 대한 금지 외에는 그다지 의미가 없습니다. .

예를 들어,라는 이름의 패키지 사이에 특별한 접근 관계가없는 oliver명명 된 다른 패키지 oliver.twist, 또는 명명 된 패키지 사이에 evelyn.wood하고 evelyn.waugh. 즉, 이름 oliver.twist이 지정된 패키지의 oliver코드는 다른 패키지의 코드보다 패키지 내에 선언 된 유형에 더 잘 액세스 할 수 없습니다 .


이러한 맥락에서 질문 자체에 답할 수 있습니다. 패키지와 해당 서브 패키지 또는 상위 패키지의 두 개의 서로 다른 서브 패키지 사이에는 명시 적으로 특별한 액세스 관계가 없으므로 요청 된 방식으로 두 개의 서로 다른 패키지에 메소드를 표시 할 수있는 방법이 없습니다. 이것은 문서화 된 의도적 인 디자인 결정입니다.

메소드를 공개하고 모든 패키지 ( odp.proj및 포함 odp.proj.test)가 지정된 메소드에 액세스 할 수 있거나 메소드를 패키지 전용 (기본 가시성)으로 만들 수 있으며 메소드에 직접 액세스해야하는 모든 코드를 입력해야합니다. 방법과 동일한 (하위) 패키지.

즉, Java의 매우 표준 사례는 테스트 코드를 소스 코드와 동일한 패키지에 배치하지만 파일 시스템의 다른 위치에 배치하는 것입니다. 예를 들어,에 메이븐 빌드 도구,이 협약은 이러한 소스 및 테스트 파일을 배치하는 것 src/main/java/odp/projsrc/test/java/odp/proj각각. 빌드 도구가이를 컴파일하면 두 파일 세트가 모두 odp.proj패키지에 포함되지만 src파일 만 프로덕션 아티팩트에 포함됩니다. 테스트 파일은 빌드시에만 프로덕션 파일을 확인하는 데 사용됩니다. 이 설정을 통해 테스트 코드는 테스트중인 코드의 패키지 개인 코드 또는 보호 된 코드가 동일한 패키지에 있으므로 자유롭게 액세스 할 수 있습니다.

테스트 / 제작 사례가 아닌 하위 패키지 또는 형제 패키지에서 코드 공유를 원하는 경우 일부 라이브러리에서 사용하는 한 가지 해결책은 공유 코드를 공개로 설정하는 것이지만 내부 라이브러리 용으로 작성되었다는 것을 문서화하는 것입니다 사용하십시오.


0

메소드 앞에 액세스 수정자를 넣지 않으면 개인용 패키지라고합니다.
다음 예를보십시오.

package odp.proj;
public class A
{
    void launchA() { }
}

package odp.proj.test;
public class B
{
    void launchB() { }
}

public class Test
{
    public void test()
    {
        A a = new A();
        a.launchA()    // cannot call launchA because it is not visible
    }
}

0

PackageVisibleHelper 클래스를 사용하여 PackageVisibleHelperFactory가 고정되기 전에 비공개로 유지하면 어디서나 launchA (by PackageVisibleHelper) 메소드를 호출 할 수 있습니다.)

package odp.proj;
public class A
 {
    void launchA() { }
}

public class PackageVisibleHelper {

    private final PackageVisibleHelperFactory factory;

    public PackageVisibleHelper(PackageVisibleHelperFactory factory) {
        super();
        this.factory = factory;
    }

    public void launchA(A a) {
        if (factory == PackageVisibleHelperFactory.INSTNACNE && !factory.isSampleHelper(this)) {
            throw new IllegalAccessError("wrong PackageVisibleHelper ");
        }
        a.launchA();
    }
}


public class PackageVisibleHelperFactory {

    public static final PackageVisibleHelperFactory INSTNACNE = new PackageVisibleHelperFactory();

    private static final PackageVisibleHelper HELPER = new PackageVisibleHelper(INSTNACNE);

    private PackageVisibleHelperFactory() {
        super();
    }

    private boolean frozened;

    public PackageVisibleHelper getHelperBeforeFrozen() {
        if (frozened) {
            throw new IllegalAccessError("please invoke before frozen!");
        }
        return HELPER;
    }

    public void frozen() {
        frozened = true;
    }

    public boolean isSampleHelper(PackageVisibleHelper helper) {
        return HELPER.equals(helper);
    }
}
package odp.proj.test;

import odp.proj.A;
import odp.proj.PackageVisibleHelper;
import odp.proj.PackageVisibleHelperFactory;

public class Test {

    public static void main(String[] args) {

        final PackageVisibleHelper helper = PackageVisibleHelperFactory.INSTNACNE.getHelperBeforeFrozen();
        PackageVisibleHelperFactory.INSTNACNE.frozen();


        A a = new A();
        helper.launchA(a);

        // illegal access       
        new PackageVisibleHelper(PackageVisibleHelperFactory.INSTNACNE).launchA(a); 
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.