2008-10-08 7 views

답변

40

'친구'개념은 예를 들어 API를 구현과 분리하는 데 유용한 Java에서 유용합니다. 구현 클래스는 API 클래스 내부에 대한 액세스가 필요하지만 API 클라이언트에는 공개되어서는 안됩니다.API를 통해 노출

클래스 : 아래에 설명 된대로이 작업은 '친구 액세서'패턴을 사용하여 달성 할 수

package api; 

public final class Exposed { 
    static { 
     // Declare classes in the implementation package as 'friends' 
     Accessor.setInstance(new AccessorImpl()); 
    } 

    // Only accessible by 'friend' classes. 
    Exposed() { 

    } 

    // Only accessible by 'friend' classes. 
    void sayHello() { 
     System.out.println("Hello"); 
    } 

    static final class AccessorImpl extends Accessor { 
     protected Exposed createExposed() { 
      return new Exposed(); 
     } 

     protected void sayHello(Exposed exposed) { 
      exposed.sayHello(); 
     } 
    } 
} 

'친구'기능을 제공하는 클래스 :

package impl; 

public abstract class Accessor { 

    private static Accessor instance; 

    static Accessor getInstance() { 
     Accessor a = instance; 
     if (a != null) { 
      return a; 
     } 

     return createInstance(); 
    } 

    private static Accessor createInstance() { 
     try { 
      Class.forName(Exposed.class.getName(), true, 
       Exposed.class.getClassLoader()); 
     } catch (ClassNotFoundException e) { 
      throw new IllegalStateException(e); 
     } 

     return instance; 
    } 

    public static void setInstance(Accessor accessor) { 
     if (instance != null) { 
      throw new IllegalStateException(
       "Accessor instance already set"); 
     } 

     instance = accessor; 
    } 

    protected abstract Exposed createExposed(); 

    protected abstract void sayHello(Exposed exposed); 
} 

예 '친구'구현 패키지의 클래스에서 액세스 :

package impl; 

public final class FriendlyAccessExample { 
    public static void main(String[] args) { 
     Accessor accessor = Accessor.getInstance(); 
     Exposed exposed = accessor.createExposed(); 
     accessor.sayHello(exposed); 
    } 
} 
+1

"Exposed"클래스의 "정적"의미를 알지 못했기 때문에 : 정적 블록은 클래스가 JVM에 처음로드 될 때 실행되는 Java 클래스의 문 블록입니다 자세히보기 http://www.javatutorialhub.com/java-static-variable-methods.html#VlA0qtKsqfFuFo8r.99 –

+0

재미있는 패턴이지만 노출 된 클래스와 접근 자 클래스가 공개되어야하는 반면 API를 구현하는 클래스 (즉, 공용 Java 인터페이스 세트를 구현하는 Java 클래스)는 "기본 보호"가 더 좋으며 따라서 클라이언트가 유형을 구현과 분리 할 수 ​​없습니다. –

+8

나는 자바에 녹슬지 않아 내 무지를 용서한다.Salomon BRYS가 게시 한 "Romeo and Juliet"솔루션의 이점은 무엇입니까? 이 구현은 내가 코드베이스에서 (당신의 설명이 첨부되지 않은 채, 무거운 논평없이) 비틀어지면 내 바지를 겁 먹을 것이다. 로미오와 줄리엣 방식은 이해하기가 매우 쉽습니다. – Steazy

0

키워드를 사용하고 있지 않습니다.

반사 등을 사용하여 "속이려는"경우가 있지만 '부정 행위'는 권장하지 않습니다.

+3

나는 이것이 내게 혐오 스럽다는 그런 나쁜 생각을 고려할 것입니다. 명백하게 이것은 최고로 헛소리이며 어떤 디자인에도 속해서는 안됩니다. – shsteimer

+0

성능 문제에 대해 이야기하지 않고 바로 @shsteimer ... – Snicolas

7

내가 아는 한, 불가능합니다.

아마도 디자인에 대한 자세한 내용을 알려줄 수 있습니다. 이와 같은 질문은 설계상의 결함으로 인한 것일 수 있습니다. 그들은 매우 밀접하게 관련되어있는 경우

그냥,

  • 왜 다른 패키지에서 이러한 클래스 고려해야?
  • B의 개인 멤버에 액세스하려면 A가 있습니까? 아니면 A 클래스에 의해 트리거되고 B 클래스로 이동해야합니까?
  • 정말 전화를 걸거나 이벤트 처리가 더 좋습니까?
0

보호 된 메소드에 액세스하려는 경우 공개하려는 (또는보다 안전한 네임 스페이스의 내부) 메소드를 노출하는 사용할 클래스의 서브 클래스를 작성할 수 있으며, (프록시로 사용).

개인적인 방법에 관한 한 (나는 생각합니다) 운이 없다.

50

Java 디자이너는 C++에서 작동하는 친구의 아이디어를 명시 적으로 거부했습니다. 당신은 당신의 "친구들"을 같은 패키지에 넣습니다. 개인, 보호 및 패키지 보안은 언어 디자인의 일부로 시행됩니다.

James Gosling은 Java가 실수없이 C++이되기를 원했습니다. 나는 친구가 OOP 원칙을 위반했기 때문에 실수라고 느꼈다고 믿는다. 패키지는 OOP에 대해 너무 순진하지 않으면 서 구성 요소를 구성하는 합리적인 방법을 제공합니다.

NR은 리플렉션을 사용하여 속일 수 있다고 지적했으나 SecurityManager를 사용하지 않는 경우에만 작동합니다. 자바 표준 보안을 켜면 보안 정책을 작성하여 특별히 허용하지 않는 한 리플렉션으로 속임수를 쓸 수 없습니다.

+11

저는 보행자가되는 것을 의미하지 않지만 액세스 수정자는 보안 메커니즘이 아닙니다. –

+5

액세스 수정자는 java 보안 모델의 일부입니다. 나는 특별히 reflection을위한 java.lang.RuntimePermission을 언급했다 : accessDeclaredMembers와 accessClassInPackage. –

+48

Gosling이 정말로 '친구'가 OOP를 위반했다고 생각한다면 (특히 패키지 액세스 이상) [실제로 이해하지 못했습니다] (http://www.parashift.com/c++-faq-lite/friends. html # faq-14.2) (전적으로 가능하다. 많은 사람들이 그것을 오해한다.) –

-1

나는 리플렉션을 사용하여 런타임에 "친구 확인"을 한 리플렉션 기반 솔루션을 보았고 호출 스택을 검사하여 메소드를 호출하는 클래스가 그렇게하도록 허용되었는지 확인했습니다. 런타임 검사이기 때문에 분명한 단점이 있습니다.

0

필자는 퍼블릭 클래스가되지 않도록 위임이나 컴포지션 또는 팩토리 클래스 (이 문제로 인해 발생하는 문제에 따라 다름)를 선호합니다.

"다른 패키지의 인터페이스/구현 클래스"문제인 경우 impl 패키지와 동일한 패키지에서 impl 클래스의 노출을 방지하는 공용 팩토리 클래스를 사용합니다.

"이 클래스/메서드를 다른 패키지의 다른 클래스에 제공하는 것이 싫지만"같은 패키지에서 공용 대리자 클래스를 사용하고 "외부인"클래스에서 필요로하는 기능의 일부

이러한 결정 중 일부는 대상 서버 클래스 로딩 아키텍처 (OSGi 번들, WAR/EAR 등), 배포 및 패키지 이름 지정 규칙에 의해 결정됩니다. 예를 들어, 위의 제안 된 솔루션 인 'Friend Accessor'패턴은 일반 Java 응용 프로그램에서 영리합니다. 클래스 로딩 스타일의 차이로 인해 OSGi에서 구현하기가 까다로워지는 지 궁금합니다.

2

제공되는 솔루션이 아마도 가장 간단한 것은 아닙니다. 또 다른 접근 방식은 C++에서와 같은 아이디어에 기반합니다. 개인 멤버는 패키지/개인 범위 외부에서 액세스 할 수 없습니다. 단, 소유자가 자체적으로 친구를 만드는 특정 클래스는 예외입니다.

구성원에 대한 친구 액세스가 필요한 클래스는 숨겨진 속성을 소유하고있는 클래스가 액세스 구현 메서드를 구현하는 하위 클래스를 반환하여 액세스를 내보낼 수있는 내부 공용 추상 "친구 클래스"를 만들어야합니다. friend 클래스의 "API"메소드는 private 일 수 있으므로 친구 액세스가 필요한 클래스 외부에서는 액세스 할 수 없습니다. 유일한 선언문은 내보내기 클래스가 구현하는 추상 보호 된 멤버에 대한 호출입니다.

먼저 시험이 실제로 작동하는지 확인합니다 : 여기

코드의

다음
package application; 

import application.entity.Entity; 
import application.service.Service; 
import junit.framework.TestCase; 

public class EntityFriendTest extends TestCase { 
    public void testFriendsAreOkay() { 
     Entity entity = new Entity(); 
     Service service = new Service(); 
     assertNull("entity should not be processed yet", entity.getPublicData()); 
     service.processEntity(entity); 
     assertNotNull("entity should be processed now", entity.getPublicData()); 
    } 
} 

법인의 패키지 private 멤버에 친구 액세스를 필요로하는 서비스 :

package application.service; 

import application.entity.Entity; 

public class Service { 

    public void processEntity(Entity entity) { 
     String value = entity.getFriend().getEntityPackagePrivateData(); 
     entity.setPublicData(value); 
    } 

    /** 
    * Class that Entity explicitly can expose private aspects to subclasses of. 
    * Public, so the class itself is visible in Entity's package. 
    */ 
    public static abstract class EntityFriend { 
     /** 
     * Access method: private not visible (a.k.a 'friendly') outside enclosing class. 
     */ 
     private String getEntityPackagePrivateData() { 
      return getEntityPackagePrivateDataImpl(); 
     } 

     /** contribute access to private member by implementing this */ 
     protected abstract String getEntityPackagePrivateDataImpl(); 
    } 
} 

마지막으로, 패키지 private 멤버에게 application.service.Service 클래스에만 친숙하게 액세스 할 수있는 Entity 클래스입니다.

package application.entity; 

import application.service.Service; 

public class Entity { 

    private String publicData; 
    private String packagePrivateData = "secret"; 

    public String getPublicData() { 
     return publicData; 
    } 

    public void setPublicData(String publicData) { 
     this.publicData = publicData; 
    } 

    String getPackagePrivateData() { 
     return packagePrivateData; 
    } 

    /** provide access to proteced method for Service'e helper class */ 
    public Service.EntityFriend getFriend() { 
     return new Service.EntityFriend() { 
      protected String getEntityPackagePrivateDataImpl() { 
       return getPackagePrivateData(); 
      } 
     }; 
    } 
} 

좋아, 나는 "friend service :: Service;"보다 약간 더 길다는 것을 인정해야한다. 어노테이션을 사용하여 컴파일 타임 검사를 유지하면서 코드를 단축 할 수 있습니다.

+0

동일한 패키지에서 일반 클래스처럼 getFriend()를 호출 한 다음 개인 메소드를 우회하는 보호 된 메소드를 호출 할 수는 없습니다. – user2219808

1

자바에서는 "패키지 관련 우호"가 가능합니다. 단위 테스트에 유용 할 수 있습니다. 메소드 앞에 private/public/protected를 지정하지 않으면 "패키지의 친구"가됩니다. 동일한 패키지에있는 클래스는 액세스 할 수 있지만 클래스 외부에서는 비공개가됩니다.

이 규칙은 항상 알려진 것이 아니며 C++ "friend"키워드의 좋은 근사치입니다. 좋은 대체품입니다.

+1

사실입니다.하지만 다른 패키지에있는 코드에 대해 정말 묻고있었습니다 ... –

9

동일한 패키지에 모든 클래스를 유지하는 것과 관련된 두 가지 해결책이 있습니다.

첫 번째는 (Practical API Design, Tulach 2008)에 설명 된 Friend Accessor/Friend Package 패턴을 사용하는 것입니다.

둘째는 OSGi를 사용하는 것입니다. OSGi가이를 어떻게 수행하는지 설명하는 문서 here이 있습니다.

관련 질문 : 1, 23

+1

+1 액세서 링크. – RustyTheBoyRobot

2

eirikma의 답변은 쉽고 뛰어납니다. 한 가지 더 추가 할 수 있습니다. 공개적으로 액세스 할 수있는 메서드 인 getFriend() 대신 사용할 수없는 친구를 얻으려면 한 단계 더 나아가 토큰없이 친구를 얻지 못하게 할 수 있습니다 : getFriend (Service.FriendToken). 이 FriendToken은 전용 생성자가있는 내부 공용 클래스이므로 서비스 만 인스턴스화 할 수 있습니다.

3

C++의 친구 클래스는 Java의 내부 클래스 개념과 같다고 생각합니다. 내부 클래스를 사용하여 실제로는 둘러싸는 클래스와 동봉 된 클래스를 정의 할 수 있습니다. 봉쇄 된 클래스는 클래스를 둘러싸는 public 멤버와 private 멤버에게 완전히 액세스 할 수 있습니다. 다음 링크를 참조하십시오. http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

+0

어, 아니, 그렇지 않아. 실생활에서 우정과 더 비슷합니다. 상호 적이 될 수는 있지만 그렇게 할 필요는 없습니다. (B가 B가 A의 친구라고 생각하지는 않습니다.) 당신과 당신의 친구들은 완전히 다른 사람 일 수 있습니다. 가족이 있고 자신 만의 (아마도 꼭 그런 것은 아님) 친구의 서클을 가질 수 있습니다. (많은 친구들과 함께 수업을 듣고 싶지는 않지만 유용한 기능이 될 수 있지만주의해서 사용해야합니다.) –

0

나는 대부분의 경우 friend 키워드가 필요 없다는 데 동의합니다. 내부에 액세스하려는 디버그 클래스

  • , 나는 보통 방법은 개인 및 액세스를 통해 만들 패키지 - 개인 (일명. 기본값)이 많이 얽혀 클래스의 그룹이 대부분의 경우에 충분

    • 반사. 보통 속도는 중요하지 않습니다.
    • "해킹"또는 기타 변경 될 수있는 방법을 구현하는 경우가 있습니다. public으로 설정하고 @Deprecated를 사용하여이 메서드를 사용하지 말아야 함을 나타냅니다.

    마지막으로 실제로 필요한 경우 다른 답변에서 언급 한 친구 수락 자 패턴이 있습니다.

  • 359

    다음은 Java에서 C++ 친구 메커니즘을 복제하는 데 사용하는 작은 트릭입니다.

    내가 한 클래스가 Romeo이고 다른 클래스가 Juliet 인 경우를 가정 해 보겠습니다. 그들은 증오의 이유로 다른 패키지 (가족)에 있습니다.

    RomeocuddleJulietJulietRomeocuddle 그녀를 보자 싶어 싶어.

    C++에서 JulietRomeo을 (연인) friend으로 선언하지만 java에는 그런 것이 없습니다.

    숙녀 첫째 : 여기

    클래스와 트릭입니다
    package capulet; 
    
    import montague.Romeo; 
    
    public class Juliet { 
    
        public static void cuddle(Romeo.Love l) { 
         l.hashCode(); 
         System.out.println("O Romeo, Romeo, wherefore art thou Romeo?"); 
        } 
    
    } 
    

    그래서 방법 Juliet.cuddlepublic하지만 당신은 그것을 호출 할 Romeo.Love이 필요합니다. 이 Romeo.Love을 "시그니처 보안"으로 사용하여 만이이 메서드를 호출하고 hashCode을 호출하기 때문에 null 인 경우 런타임에 NullPointerException이 throw됩니다.

    이제

    소년 :

    package montague; 
    
    import capulet.Juliet; 
    
    public class Romeo { 
        public static final class Love { private Love() {} } 
        private static final Love love = new Love(); 
    
        public static void cuddleJuliet() { 
         Juliet.cuddle(love); 
        } 
    } 
    

    클래스 Romeo.Love 공공이지만, 생성자가 private입니다. 따라서 누구나 볼 수 있지만, Romeo 만 만들 수 있습니다. 정적 참조를 사용하므로 절대 사용되지 않는 Romeo.Love은 한 번만 생성되며 최적화에 영향을주지 않습니다. 따라서

    , Romeocuddle Juliet 만 그는 단지 그가 (그녀가 NullPointerException 당신을 때릴거야 다른 사람 또는) cuddleJuliet 그녀를 요구하는 Romeo.Love 인스턴스를 구성하고 액세스 할 수 있기 때문이다.

    +0

    후속 조치 : 저는 Java newb이므로 추측하겠습니다. "cuddle"에 대한 "Romeo.Love"매개 변수가 null 일 수 없도록하는 데 사용할 수있는 속성 (주석?)? 많은 C 컴파일러에서 '__attribute __ ((nonnull))'과 비슷한 것이 있습니까? 그게 완벽한 해결책이 될거야. (아직도 끝내주는 것처럼). – Steazy

    +73

    +1 "slap you with a NullPointerException". 매우 인상적. – Nickolas

    +2

    @Steazy 있음 : NotNull, NonNull 및 CheckForNull 주석을 찾습니다. 이러한 주석을 사용하고 적용하는 방법은 IDE 설명서를 참조하십시오. 나는 IntelliJ가 기본적으로 이것을 내장하고 Eclipse에 FindBugs와 같은 플러그인이 필요하다는 것을 알고있다. –

    0

    나는이 문제를 해결하기 위해 찾은하는 방법과 같이, 접근 객체를 생성하는 것입니다

    class Foo { 
        private String locked; 
    
        /* Anyone can get locked. */ 
        public String getLocked() { return locked; } 
    
        /* This is the accessor. Anyone with a reference to this has special access. */ 
        public class FooAccessor { 
         private FooAccessor(){}; 
         public void setLocked(String locked) { Foo.this.locked = locked; } 
        } 
        private FooAccessor accessor; 
    
        /** You get an accessor by calling this method. This method can only 
        * be called once, so calling is like claiming ownership of the accessor. */ 
        public FooAccessor getAccessor() { 
         if (accessor != null) 
          throw new IllegalStateException("Cannot return accessor more than once!"); 
         return accessor = new FooAccessor(); 
        } 
    } 
    

    첫 번째 코드는 getAccessor()이 접근의 "소유권을 주장"전화. 대개이 객체를 만드는 코드입니다. 클래스마다 수준에 반대로, 당신은 인스턴스 당 수준에 대한 액세스를 제한 할 수 있기 때문에

    Foo bar = new Foo(); //This object is safe to share. 
    FooAccessor barAccessor = bar.getAccessor(); //This one is not. 
    

    이것은 또한, C++의 친구 메커니즘에 비해 장점이있다. 접근 자 참조를 제어하면 객체에 대한 액세스를 제어 할 수 있습니다. 당신을

    class Foo { 
        private String secret; 
        private String locked; 
    
        /* Anyone can get locked. */ 
        public String getLocked() { return locked; } 
    
        /* Normal accessor. Can write to locked, but not read secret. */ 
        public class FooAccessor { 
         private FooAccessor(){}; 
         public void setLocked(String locked) { Foo.this.locked = locked; } 
        } 
        private FooAccessor accessor; 
    
        public FooAccessor getAccessor() { 
         if (accessor != null) 
          throw new IllegalStateException("Cannot return accessor more than once!"); 
         return accessor = new FooAccessor(); 
        } 
    
        /* Super accessor. Allows access to secret. */ 
        public class FooSuperAccessor { 
         private FooSuperAccessor(){}; 
         public String getSecret() { return Foo.this.secret; } 
        } 
        private FooSuperAccessor superAccessor; 
    
        public FooSuperAccessor getAccessor() { 
         if (superAccessor != null) 
          throw new IllegalStateException("Cannot return accessor more than once!"); 
         return superAccessor = new FooSuperAccessor(); 
        } 
    } 
    

    마지막으로, 당신은 좀 더 조직적으로 일을하려는 경우 : 당신은 또한 여러 접근을 만들고, 어떤 액세스 할 수있는 코드를 통해 세밀하게 제어 할 수 있도록 각각 다른 액세스를 제공 할 수 있습니다 모든 것을 함께 유지하는 참조 객체를 만들 수 있습니다. 이렇게하면 모든 접근자를 하나의 메서드 호출로 요구할 수있을뿐만 아니라 연결된 인스턴스와 함께 유지할 수 있습니다. 당신이 참조가되면, 당신이 그것을 필요로하는 코드를 밖으로 접근을 전달할 수 있습니다 많은 머리 두드리는 (안 좋은 종류) 후

    class Foo { 
        private String secret; 
        private String locked; 
    
        public String getLocked() { return locked; } 
    
        public class FooAccessor { 
         private FooAccessor(){}; 
         public void setLocked(String locked) { Foo.this.locked = locked; } 
        } 
        public class FooSuperAccessor { 
         private FooSuperAccessor(){}; 
         public String getSecret() { return Foo.this.secret; } 
        } 
        public class FooReference { 
         public final Foo foo; 
         public final FooAccessor accessor; 
         public final FooSuperAccessor superAccessor; 
    
         private FooReference() { 
          this.foo = Foo.this; 
          this.accessor = new FooAccessor(); 
          this.superAccessor = new FooSuperAccessor(); 
         } 
        } 
    
        private FooReference reference; 
    
        /* Beware, anyone with this object has *all* the accessors! */ 
        public FooReference getReference() { 
         if (reference != null) 
          throw new IllegalStateException("Cannot return reference more than once!"); 
         return reference = new FooReference(); 
        } 
    } 
    

    을이 나는 것처럼 매우 내 최종 솔루션이었고, . 유연하고 사용이 간편하며 클래스 액세스를 매우 잘 제어 할 수 있습니다. (은 참조 만 액세스가 매우 유용합니다. 접근 자/참조에 private 대신 protected를 사용하면 Foo의 하위 클래스도 getReference의 확장 참조를 반환 할 수 있습니다. 또한 반사가 필요하지 않으므로 모든 환경에서 사용할 수 있습니다.

    2

    다른 답변의 아이디어를 사용하여 여기서 재사용 가능한 Friend 클래스로 끝나는 명확한 사용 예를 자세히 설명합니다. 이 메커니즘의 이점은 친구 클래스 만이 친구가 될 수 있으며 하위 클래스는 허용되지 않는다는 것입니다.잠재적으로 더 안전하게 만듭니다.

    시작하려면 다음은 Friend 클래스를 사용하는 방법의 예입니다.

    public class Owner { 
        private final String member = "value"; 
    
        public String getMember(final Friend friend) { 
         // Make sure only a friend is accepted. 
         friend.is(Other.class); 
         return member; 
        } 
    } 
    

    그런 다음 다른 패키지에 당신은이 작업을 수행 할 수 있습니다 다음과 같이

    public class Other { 
        private final Friend friend = new Friend(this); 
        public void test() { 
         String s = new Owner().getMember(friend); 
         System.out.println(s); 
        } 
    } 
    

    Friend 클래스입니다. (당신은 한 번만 코드를 작성할 필요, 필요 주위를 복사 없습니다.)

    public final class Friend { 
        private final Class is; 
    
        public Friend(final Object is) { 
         this.is = is.getClass(); 
        } 
    
        public void is(final Class... classes) { 
         for (final Class c : classes) 
          if (c == is) 
           return; 
          throw new ClassCastException(String.format("%s is not an expected friend.", is.getName())); 
        } 
    } 
    
    1

    내 생각, 친구의 접근 패턴을 사용하는 방법이 너무 복잡하다. 저도 같은 문제에 직면했고 나는 자바에서 C++에서 알려진 좋은 오래된 복사 생성자, 사용하여 해결 :

    public class MyApp { 
    
        private static class ProtectedAccessor extends ProtectedContainer { 
    
         protected ProtectedAccessor() { 
          super(); 
         } 
    
         protected PrivateAccessor(ProtectedContainer prot) { 
          super(prot); 
         } 
    
         public String exposeProtected() { 
          return iwantAccess; 
         } 
        } 
    } 
    

    : 다음과 같은 코드를 작성할 수있는 응용 프로그램에서

    public class ProtectedContainer { 
        protected String iwantAccess; 
    
        protected ProtectedContainer() { 
         super(); 
         iwantAccess = "Default string"; 
        } 
    
        protected ProtectedContainer(ProtectedContainer other) { 
         super(); 
         this.iwantAccess = other.iwantAccess; 
        } 
    
        public int calcSquare(int x) { 
         iwantAccess = "calculated square"; 
         return x * x; 
        } 
    } 
    

    을 이 방법의 장점은 응용 프로그램 만 보호 된 데이터에 액세스 할 수 있다는 것입니다. friend 키워드를 대체하는 것이 아닙니다. 하지만 사용자 지정 라이브러리를 작성하고 보호 된 데이터에 액세스해야 할 때 매우 적합하다고 생각합니다.

    ProtectedContainer의 인스턴스를 처리해야 할 때마다 ProtectedAccessor를 둘러 싸면 액세스 할 수 있습니다.

    또한 보호 된 메소드와도 작동합니다. API에서 보호 된 것을 정의하십시오. 나중에 응용 프로그램에서 개인 랩퍼 클래스를 작성하고 보호 된 메소드를 공용으로 표시하십시오. 그게 전부 야.

    +0

    그러나'ProtectedContainer'는 패키지 외부에서 서브 클래 싱 될 수 있습니다! – Raphael

    0

    Java 9부터 모듈은 많은 경우이 문제를 해결하는 데 사용할 수 있습니다.

    관련 문제