Java에서 보호 된 멤버가 동일한 패키지의 클래스에 액세스 할 수있는 이유는 무엇입니까?


14

공식 문서에서 ...

수정 자 클래스 패키지 서브 클래스 월드 
공개 YYYY 
보호 된 YYYN 
수정 자 없음 YYNN 
개인 YNNN 

문제는 동일한 패키지 내의 클래스에서 보호 된 멤버에 액세스 해야하는 유스 케이스가 있다는 것을 기억할 수 없다는 것입니다.

이 구현의 이유는 무엇입니까?

편집 : 명확히하기 위해 동일한 패키지 내의 하위 클래스와 클래스 모두 보호 된 필드 또는 메소드에 액세스 해야하는 특정 사용 사례를 찾고 있습니다.

package some.package;
public class A {
 protected void protectedMethod(){
  // do something
 }
}

package another.package;
public class B extends A{
 public void someMethod(){
  // accessible because B is a subclass of A
  protectedMethod();
 }
} 

package some.package;
public class C {
 public void anotherMethod(){
  // accessible because C is in the same package as A
  protectedMehtod();
 }
}

답변:


4

동일한 패키지 내의 서브 클래스와 클래스 모두 보호 된 필드 나 메소드에 액세스해야하는 특정 사용 사례를 찾고 있습니다.

나에게, 그러한 유스 케이스는 특정 것보다 일반적이며, 내 선호도에서 비롯됩니다.

  1. 필요에 따라 나중에 더 약한 것을 사용하여 가능한 한 엄격한 액세스 수정 자로 시작하십시오.
  2. 단위 테스트는 테스트 코드와 동일한 패키지에 있습니다.

위에서부터 기본 액세스 수정자를 사용하여 객체 디자인을 시작할 수 있습니다 (시작 private하지만 단위 테스트가 복잡합니다).

public class Example {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        void doSomething() {} // default access
    }
    static class Unit2 {
        void doSomething() {} // default access
    }

    static class UnitTest {
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

사이드 노트는 위의 코드에서, Unit1, Unit2하고 UnitTest있다 중첩 내에서 Example프리젠 테이션의 편의를 위해,하지만 실제 프로젝트에서, 나는 가능성이 별도의 파일 (그리고 이러한 클래스를했을 UnitTest경우에도 별도의 디렉토리에 ).

그런 다음, 필연적으로 필요한 경우 액세스 제어를 기본값에서 protected다음과 같이 약화시킵니다 .

public class ExampleEvolved {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        protected void doSomething() {} // made protected
    }
    static class Unit2 {
        protected void doSomething() {} // made protected
    }

    static class UnitTest {
        // ---> no changes needed although UnitTest doesn't subclass
        // ...and, hey, if I'd have to subclass... which one of Unit1, Unit2?
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

ExampleEvolved객체 액세스가 하위 클래스 가 아니더라도 동일한 패키지에서 보호 된 메소드에 액세스 할 수 있기 때문에 단위 테스트 코드를 변경하지 않고 유지할 수 있습니다 .

적은 변경이 필요합니다 => 더 안전한 수정; 결국 액세스 수정 자 만 변경하고 어떤 메소드 Unit1.doSomething()와 방법을 수정하지 않았 Unit2.doSomething()으므로 단위 테스트 코드가 수정없이 계속 실행될 것으로 기대하는 것은 당연합니다.


5

나는 이것이 두 부분으로 이루어져 있다고 말하고 싶다.

  1. 클래스 "패키지"액세스는 클래스가 항상 적절한 캡슐화 단위가 아니기 때문에 광범위한 경우에 유용합니다. 일부 오브젝트가 다른 오브젝트의 콜렉션으로 작동하는 다양한 복합 오브젝트이지만 전체 콜렉션에 대해 일부 변형이 있으므로 콜렉션이 항목에 대한 액세스 권한을 높여야하므로 항목을 공개적으로 수정할 수 없어야합니다. C ++에는 친구가 있고 Java에는 패키지 액세스가 있습니다.
  2. 이제 "패키지"액세스 범위는 기본적으로 "서브 클래스"(보호 된) 범위와 독립적입니다. 따라서 패키지 전용, 서브 클래스 전용 및 패키지 및 서브 클래스에 대한 추가 액세스 지정자가 필요합니다. "패키지"범위는 패키지의 클래스 집합이 일반적으로 한정되어 있지만 하위 클래스가 어디에나 나타날 수 있으므로 더 제한적입니다. 따라서 일을 단순하게 유지하기 위해 Java는 단순히 보호 된 액세스에 패키지 액세스를 포함하며 패키지는 아닌 서브 클래스에 대한 추가 지정자를 갖지 않습니다. 거의 항상 protected정확히 그렇게 생각해야 합니다.

protected서브 클래스 만 있다면 더 간단하지 않습니까? 솔직히 오랫동안, 나는 그것이 그 행동이라는 인상을
받았습니다

@ jramoyo : 아니요, 결합 된 동작을 어떻게 든 사용할 수 있어야하기 때문에 다른 지정자를 의미합니다.
Jan Hudec

6
@jramoyo-C #에서는 protected클래스 및 하위 클래스 internal이며 라이브러리 / 패키지 전체입니다. 또한 protected internalJava와 동등한 것을 가지고 protected있습니다.
Bobson

@Bobson-감사합니다. C # 구현이 더 나은 선택 인 것 같습니다
jramoyo

5

IMHO, 이것은 Java의 잘못된 디자인 결정이었습니다.

단지 추측하지만 액세스 수준이 개인 전용- "패키지"-보호-공용으로 엄격히 진행되기를 원한다고 생각합니다. 일부 필드는 패키지에는 사용할 수 있지만 서브 클래스에는 사용할 수없고 일부는 서브 클래스에는 사용할 수 있지만 패키지에는 사용할 수없는 계층 구조를 원하지 않았습니다.

그럼에도 불구하고 저의 겸손한 견해로 그들은 다른 방식으로 나아갔습니다. protected는 클래스와 하위 클래스에서만 볼 수 있고 패키지는 클래스, 하위 클래스 및 패키지에서 볼 수 있다고 말했습니다.

하위 클래스는 액세스 할 수 있어야하지만 나머지 패키지는 액세스 할 수없는 클래스의 데이터가 종종 있습니다. 나는 그 반대의 경우를 생각하기가 어려웠다. 데이터를 공유해야하지만 번들 외부에서 데이터를 안전하게 유지 해야하는 상호 관련된 클래스 번들이있는 드문 경우가 있습니다. 패키지에 데이터를 넣는 것을 한번도 본 적이 없지만 서브 클래스로부터 보호하고 싶었습니다. 좋아, 상황이 다가올 것 같아 이 패키지가 내가 알지 못하는 클래스에 의해 확장 될 수있는 라이브러리의 일부라면 클래스의 데이터를 개인용으로 만드는 것과 거의 같은 이유로 가정합니다. 그러나 수업과 그 어린이들에게만 데이터를 제공하려는 것이 더 일반적입니다.

나와 내 아이들 사이에 사적인 것들이 많이 있으며 이웃과 공유하지 않습니다. 나와 이웃 사이에 사적인 관계를 유지하고 내 아이들과 공유하지 않는 것이 거의 없습니다. :-)


0

즉시 명심해야 할 좋은 예는 패키지에서 많이 사용되는 유틸리티 클래스이지만 공개 액세스 (디스크 뒤에서 이미지 로딩-디스크에서 처리, 생성 / 파괴 클래스 등)를 원하지 않습니다. .) 대신 모든 [자바의 동등한 만드는 friend access modifier or idiom에서 C++다른 모든 클래스의 모든 것을]가 자동으로 사용할 수 있습니다.


1
유틸리티 클래스는 멤버가 아닌 전체적으로 내부 클래스의 예입니다.
Jan Hudec

0

보호 / 패키지 액세스 수정 자의 사용 사례 는 C ++의 친구 액세스 수정 자의 사용 사례와 유사합니다 .

하나의 사용 사례는 Memento 패턴을 구현할 때 입니다 .

실행 취소 조작에 대한 체크 포인트로 사용하려면 memento 오브젝트가 오브젝트의 내부 상태에 액세스하여 보유해야합니다.

Java에는 "friend" 액세스 수정자가 없으므로 동일한 패키지에서 클래스를 선언하는 것이 Memento 패턴을 달성 할 수있는 방법 중 하나 입니다 .


1
아닙니다. memento 객체는 객체에 액세스 할 필요가 없으며 액세스 할 수 없습니다. 그것은 자신을 메멘토로 직렬화하고 다시 직렬화 해제하는 객체입니다. 그리고 기념품 자체는 바보 같은 속성 가방입니다. 또한 다른 사람에게 공개적으로 액세스 할 수있는 것 이상이 없어야합니다.
Jan Hudec

@JanHudec 나는 문자 그대로 "Memento 패턴을 달성하는 가능한 방법 중 하나입니다"라고 말했습니다 .
Tulains Córdova

그리고 Memento 패턴을 달성하는 또 다른 가능한 방법은 모든 것을 공개하는 것입니다. 즉, 나는 당신의 요점을 보지 못합니다.
Thomas Eding

-1

대칭?

이러한 액세스가 필요한 경우는 드물기 때문에 기본 액세스가 거의 사용되지 않습니다. 그러나 때때로 프레임 워크는 래퍼 클래스가 클래스와 직접 상호 작용하는 동일한 패키지에 배치되어 성능상의 이유로 접근자가 아닌 멤버로 이동하는 생성 된 코드에 대해이를 원합니다. GWT를 생각하십시오.


1
고마워요, 예가 있습니까? 그런 소리는 "보호 된"것이 아니라 "기본"접근 자에 의해 달성 될 수 있습니다
jramoyo

-1

Java의 캡슐화 계층은 다음과 같이 잘 정의되어 있습니다.

클래스-> 패키지-> 상속

"보호"는 Java 디자이너가 결정한 패키지 기본값보다 약한 개인 정보 보호 형식입니다. 패키지 기본 항목에 대한 액세스는 보호 된 항목에 액세스 할 수있는 엔터티의 하위 집합으로 제한됩니다.

수학 및 구현 관점에서 중첩 될 항목에 액세스 할 수있는 엔터티 집합을 갖는 것이 의미가 있습니다. 클래스가 다른 패키지에서 상속 될 수 있기 때문에 패키지 액세스 세트를 보호 액세스 세트 내에 중첩 할 수 없습니다.

개념적인 관점에서 java.util의 하위 클래스 인 com.example.foo.bar의 다른 패키지보다 패키지의 다른 클래스와 "친숙한"것을 갖는 것이 합리적입니다. 전자의 경우 수업은 같은 저자 또는 적어도 같은 조직의 코더가 작성했을 가능성이 높습니다.


1
"약한 형태 ..." 는 무엇을 의미 합니까?
gnat
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.