Java에서 C ++ 'friend'개념을 시뮬레이션하는 방법이 있습니까?


196

한 패키지에 Java 클래스를 작성하여 다른 클래스의 서브 클래스로 만들 필요없이 다른 패키지에있는 클래스의 비공개 메소드에 액세스 할 수 있기를 원합니다. 이게 가능해?

답변:


466

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

클래스 Romeo와 다른 클래스 가 있다고 가정 해 봅시다 Juliet. 증오 때문에 다른 패키지 (패밀리)에 있습니다.

Romeo싶어 cuddle Juliet하고 Juliet만하자 싶어 Romeo cuddle그녀를.

C ++에서는 (애인)으로 Juliet선언 하지만 Java에는 그러한 것이 없습니다.Romeofriend

수업과 요령은 다음과 같습니다.

여성분 먼저 :

package capulet;

import montague.Romeo;

public class Juliet {

    public static void cuddle(Romeo.Love love) {
        Objects.requireNonNull(love);
        System.out.println("O Romeo, Romeo, wherefore art thou Romeo?");
    }

}

따라서 방법 Juliet.cuddle은 public있지만 Romeo.Love호출해야합니다. 그것은이를 사용하는 Romeo.Love경우에만 보장하기 위해 "서명 보안"으로 Romeo이 방법과 사랑이 진짜 너무 런타임이를 던질 것입니다 것을 확인 호출 할 수 있습니다 NullPointerException이 경우를 null.

이제 소년들 :

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는 public이지만 생성자는 private입니다. 따라서 누구나 볼 수 있지만 Romeo구성 할 수 있습니다. 정적 참조를 사용하므로 Romeo.Love결코 사용되지 않는 것은 한 번만 구성되며 최적화에 영향을 미치지 않습니다.

따라서,이 Romeo수 cuddle Juliet만 그 때문에 단지 그는 구성하고 액세스 할 수 있습니다 Romeo.Love에 필요한 인스턴스 Juliet에 cuddle그녀 (또는 다른 그녀가 당신을 때릴 것을 NullPointerException).


107
"NullPointerException으로 때려 눕힘"으로 +1 매우 인상적.
— Nickolas

2
@Steazy 있습니다 : NotNull, NonNull 및 CheckForNull 주석을 찾으십시오. 이러한 주석을 사용하고 적용하는 방법은 IDE 설명서를 참조하십시오. IntelliJ는 기본적으로 이것을 포함하고 Eclipse에는 플러그인 (예 : FindBugs)이 필요하다는 것을 알고 있습니다.
— Salomon BRYS

27
당신은 할 수 있습니다 Romeo'들 Love에 대한 Julia변화에 의해 영원한 love으로 필드를 final;-).
— Matthias

5
@Matthias 사랑 필드는 정적입니다 ... 나는 최종 답변을 편집합니다;)
— Salomon BRYS

12
모든 대답은 유머와 훌륭한 예를 위해 (Y) +1과 같아야합니다.
— Zia Ul Rehman Mughal

54

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

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

NR은 리플렉션을 사용하여 부정 행위를 할 수 있다고 지적했지만 SecurityManager를 사용하지 않는 경우에만 작동합니다. Java 표준 보안을 설정하면 보안 정책을 작성하여 명시 적으로 허용하지 않는 한 리플렉션을 사용하여 부정 행위를 할 수 없습니다.


11
나는 pedant라는 것을 의미하지는 않지만 액세스 수정자는 보안 메커니즘이 아닙니다.
— Greg D

6
액세스 수정자는 Java 보안 모델의 일부입니다. 리플렉션을 위해 java.lang.RuntimePermission을 구체적으로 언급했습니다 : accessDeclaredMembers 및 accessClassInPackage.
— David G

54
Gosling이 friendOOP 위반 (특히, 패키지 액세스보다 많은 것)을 실제로 생각했다면, 실제로 OOP를 이해하지 못했을 것 입니다.
— Konrad Rudolph

8
클래스 구성 요소를 분리해야하는 경우가 있습니다 (예 : 구현 및 API, 핵심 객체 및 어댑터). 패키지 수준 보호는 동시에이를 허용하기에는 너무 관대하고 제한적입니다.
— dhardy

2
@GregD 개발자가 클래스 멤버를 잘못 사용하지 못하도록하는 보안 메커니즘으로 간주 될 수 있습니다. 아마도 그것들은 아마도 안전 메커니즘 이라고 더 잘 생각 합니다.
— 분쇄

45

'friend'개념은 예를 들어, API를 구현에서 분리하는 데 Java에서 유용합니다. 구현 클래스는 일반적으로 API 클래스 내부에 액세스해야하지만 API 클라이언트에 노출되어서는 안됩니다. 아래에 자세히 설명 된 'Friend Accessor'패턴을 사용하여이를 달성 할 수 있습니다.

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);
}

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

package impl;

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

1
"노출 된"클래스에서 "정적"의 의미를 몰랐기 때문에 : 정적 블록은 클래스가 JVM에 처음로드 될 때 실행될 Java 클래스 내의 명령문 블록입니다 . javatutorialhub
— Guy L

흥미로운 패턴이지만 노출 된 및 접근 자 클래스는 공개이어야하지만 API를 구현하는 클래스 (즉, 공개 Java 인터페이스 세트를 구현하는 Java 클래스 세트)는 "기본적으로 보호"되므로 클라이언트가 액세스 할 수 없습니다. 구현에서 유형을 분리합니다.
— Yann-Gaël Guéhéneuc

8
나는 내 자바에 상당히 녹슬 었으므로 무지를 용서하십시오. Salomon BRYS가 게시 한 "Romeo and Juliet"솔루션에 비해이 점의 장점은 무엇입니까? 이 구현은 코드베이스에서 비틀 거렸다면 설명을 첨부하지 않고 (즉, 무거운 주석 달기) 바지를 두려워 할 것입니다. Romeo와 Juliet의 접근 방식은 이해하기가 매우 간단합니다.
— Steazy

1
이 접근 방식은 런타임시에만 문제를 볼 수있는 반면, Romeo와 Juliet의 오용은 개발 중에 컴파일 타임에 문제를 볼 수있게합니다.
— ymajoros

1
@ymajoros Romeo와 Juliet 예제는 컴파일 타임에 오용을 보이지 않습니다. 올바르게 전달되는 인수와 예외가 발생합니다. 이것들은 모두 런타임 동작입니다.
— Radiodef

10

귀하의 질문에 대한 모든 솔루션을 동일한 패키지에 유지하는 것을 포함하지 않는 두 가지 솔루션이 있습니다.

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

두 번째는 OSGi를 사용하는 것입니다. OSGi가이를 수행하는 방법을 설명하는 기사가 있습니다 .

관련 질문 : 1 , 2 및 3 .


7

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

아마도, 당신은 당신의 디자인에 대한 좀 더 자세한 정보를 우리에게 줄 수 있습니다. 이와 같은 질문은 디자인 결함의 결과 일 수 있습니다.

그냥 고려

  • 클래스가 서로 밀접하게 관련되어있는 이유는 무엇입니까?
  • A가 B의 개인 멤버에 액세스해야합니까, 아니면 조작을 클래스 B로 이동시키고 A에 의해 트리거되어야합니까?
  • 이것이 실제로 전화를 걸거나 이벤트 처리가 더 낫습니까?

3

eirikma의 대답은 쉽고 우수합니다. 공개적으로 접근 가능한 메소드 인 getFriend ()를 사용하여 친구를 사용할 수 없게하는 대신 한 단계 더 나아가서 토큰없이 친구를 얻지 못하게 할 수 있습니다 : getFriend (Service.FriendToken). 이 FriendToken은 개인 생성자가있는 내부 공개 클래스이므로 서비스 만 인스턴스화 할 수 있습니다.


3

재사용 가능한 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 as;

    public Friend(final Object is) {
        as = is.getClass();
    }

    public void is(final Class c) {
        if (c == as)
            return;
        throw new ClassCastException(String.format("%s is not an expected friend.", as.getName()));
    }

    public void is(final Class... classes) {
        for (final Class c : classes)
            if (c == as)
                return;
        is((Class)null);
    }
}

그러나 문제는 다음과 같이 악용 될 수 있다는 것입니다.

public class Abuser {
    public void doBadThings() {
        Friend badFriend = new Friend(new Other());
        String s = new Owner().getMember(badFriend);
        System.out.println(s);
    }
}

이제 Other클래스에 공개 생성자가 없으므로 위의 Abuser코드를 불가능하게 만드는 것이 사실 일 수 있습니다 . 그러나 클래스 에 공용 생성자 가 있으면 Friend 클래스를 내부 클래스로 복제하는 것이 좋습니다. 이 Other2수업을 예로 들어 보자.

public class Other2 {
    private final Friend friend = new Friend();

    public final class Friend {
        private Friend() {}
        public void check() {}
    }

    public void test() {
        String s = new Owner2().getMember(friend);
        System.out.println(s);
    }
}

그리고 Owner2수업은 다음과 같습니다.

public class Owner2 {
    private final String member = "value";

    public String getMember(final Other2.Friend friend) {
        friend.check();
        return member;
    }
}

것을 알 수 Other2.Friend클래스 따라서이 그 일을 훨씬 더 안전하게 만들고, 개인 생성자가 있습니다.


2

제공된 솔루션은 아마도 가장 단순하지 않았습니다. 다른 접근 방식은 C ++에서와 동일한 아이디어를 기반으로합니다. 소유자가 자신을 친구로 만드는 특정 클래스를 제외하고 개인 구성원은 패키지 / 개인 범위 외부에서 액세스 할 수 없습니다.

멤버에 대한 친구 액세스가 필요한 클래스는 액세스 구현 메소드를 구현하는 서브 클래스를 리턴하여 숨겨진 특성을 소유하는 클래스가 액세스를 내보낼 수있는 내부 공개 추상 "프렌즈 클래스"를 작성해야합니다. 친구 클래스의 "API"메소드는 개인용 일 수 있으므로 친구 액세스가 필요한 클래스 외부에서는 액세스 할 수 없습니다. 유일한 설명은 내보내기 클래스가 구현하는 추상 보호 멤버에 대한 호출입니다.

코드는 다음과 같습니다.

먼저 이것이 실제로 작동하는지 확인하는 테스트 :

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());
    }
}

그런 다음 Entity의 패키지 개인 구성원에 대한 친구 액세스 권한이 필요한 서비스 :

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();
    }
}

마지막으로, 클래스 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;"보다 약간 길다는 것을 인정해야한다 그러나 주석을 사용하여 컴파일 타임 검사를 유지하면서 단축시킬 수 있습니다.


이것은 동일한 패키지의 일반 클래스처럼 작동하지 않으므로 getFriend () 한 다음 개인 메소드를 무시하고 보호 된 메소드를 호출 할 수 있습니다.
— user2219808

1

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

이 규칙은 항상 알려진 것은 아니며 C ++ "friend"키워드에 대한 근사치입니다. 나는 그것을 대체하는 것이 좋습니다.


1
이것은 사실이지만 다른 패키지에있는 코드에 대해 정말로 묻고있었습니다.
— Matthew Murdoch

1

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


아니, 아니에요 실생활에서의 우정과 더 비슷합니다. 상호 관계가 될 수는 있지만 상호 필요는 없습니다 (A가 B의 친구가된다는 것은 B가 A의 친구로 간주되는 것은 아닙니다). 가족이있을 수 있으며 자신과 겹치는 친구 그룹이있을 수 있습니다. (많은 친구들과 함께 수업을보고 싶지는 않습니다. 유용한 기능이지만주의해서 사용해야합니다.)
— Christopher Creutzig

1

친구 접근 자 패턴을 사용하는 방법이 너무 복잡하다고 생각합니다. 나는 같은 문제에 직면해야했고 Java에서 C ++로 알려진 좋은 오래된 복사 생성자를 사용하여 해결했습니다.

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;
    }
}

응용 프로그램에서 다음 코드를 작성할 수 있습니다.

public class MyApp {

    private static class ProtectedAccessor extends ProtectedContainer {

        protected ProtectedAccessor() {
            super();
        }

        protected PrivateAccessor(ProtectedContainer prot) {
            super(prot);
        }

        public String exposeProtected() {
            return iwantAccess;
        }
    }
}

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

ProtectedContainer 인스턴스를 처리해야 할 때마다 ProtectedAccessor를 감싸서 액세스 할 수 있습니다.

또한 보호 된 방법으로 작동합니다. API에서 보호되도록 정의하십시오. 나중에 응용 프로그램에서 개인 래퍼 클래스를 작성하고 보호 된 메서드를 공개로 노출합니다. 그게 다야.


1
그러나 ProtectedContainer패키지 외부에서 서브 클래 싱 할 수 있습니다!
— Raphael

0

보호 된 메소드에 액세스하려면 사용하려는 클래스의 서브 클래스를 작성하여 공용으로 사용하려는 메소드 (또는 네임 스페이스 내부에서 더 안전하도록)를 노출하고 클래스에 해당 클래스의 인스턴스를 가질 수 있습니다. (프록시로 사용).

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


0

대부분의 경우 친구 키워드가 불필요하다는 데 동의합니다.

  • 패키지-비공개 (일명 기본)는 많은 그룹으로 묶여있는 대부분의 경우 충분합니다.
  • 내부에 액세스하려는 디버그 클래스의 경우 일반적으로 메서드를 전용으로 만들고 리플렉션을 통해 액세스합니다. 여기서 속도는 일반적으로 중요하지 않습니다
  • 때로는 "해킹"이거나 변경 될 수있는 방법을 구현할 수 있습니다. 나는 공개하지만 @Deprecated를 사용하여 기존 에이 방법에 의존해서는 안된다는 것을 나타냅니다.

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


0

키워드 등을 사용하지 않습니다.

리플렉션 등을 사용하여 "속임수"를 사용할 수 있지만 "속임수"는 권장하지 않습니다.


3
나는 이것이 나에게 끔찍한 제안이라고하는 나쁜 생각이라고 생각할 것입니다. 분명히 이것은 최선의 결정이며 어떤 디자인에도 포함되어서는 안됩니다.
— shsteimer 2016

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();
    }
}

많은 종류의 헤드 뱅잉 (좋은 종류는 아님) 후에, 이것이 나의 최종 해결책이었고, 나는 그것을 매우 좋아했습니다. 유연하고 사용하기 쉬우 며 클래스 액세스를 매우 잘 제어 할 수 있습니다. (는 참조 만 접근이 매우 유용합니다.) 대신도에서 확장 된 참조를 반환 할 수 있습니다 푸의 접근 / 참조, 하위 클래스에 대한 개인의 보호를 사용하는 경우 getReference. 또한 반사가 필요하지 않으므로 모든 환경에서 사용할 수 있습니다.


0

Java 9부터는 모듈을 사용하여 많은 경우에 이것을 문제가 아닌 것으로 만들 수 있습니다.


0

나는 공개 클래스로 만들지 않기 위해 위임 또는 구성 또는 팩토리 클래스 (이 문제를 일으키는 문제에 따라)를 선호합니다.

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

"다른 클래스의 다른 클래스에이 기능을 제공하기 위해이 클래스 / 메소드를 공개하기 싫어"문제인 경우, 동일한 패키지에서 공용 델리게이트 클래스를 사용하고 해당 기능의 해당 부분 만 노출합니다 "outsider"클래스에 필요합니다.

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


0

누구에게나 쓸모가 있는지 모르겠지만 다음과 같이 처리했습니다.

인터페이스 (AdminRights)를 만들었습니다.

해당 함수를 호출 할 수 있어야하는 모든 클래스는 AdminRights를 구현해야합니다.

그런 다음 HasAdminRights 함수를 다음과 같이 만들었습니다.

private static final boolean HasAdminRights()
{
    // Gets the current hierarchy of callers
    StackTraceElement[] Callers = new Throwable().getStackTrace();

    // Should never occur with me but if there are less then three StackTraceElements we can't check
    if (Callers.length < 3)
    {
        EE.InvalidCode("Couldn't check for administrator rights");
        return false;

    } else try
    {

        // Now we check the third element as this function is the first and the function wanting to check for the rights the second. We try to use it as a subclass of AdminRights.
        Class.forName(Callers[2].getClassName()).asSubclass(AdminRights.class);

        // If everything worked up to now, it has admin rights!
        return true;

    } catch (java.lang.ClassCastException | ClassNotFoundException e)
    {
        // In the catch, something went wrong and we can deduce that the caller has no admin rights

        EE.InvalidCode(Callers[1].getClassName() + " doesn't have administrator rights");
        return false;
    }
}

-1

리플렉션을 사용하여 런타임에 "친구 확인"하고 콜 스택을 확인하여 메소드를 호출하는 클래스가 허용되는지 확인한 리플렉션 기반 솔루션을 본 적이 있습니다. 런타임 검사이므로 명백한 단점이 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.