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.cuddlepublic있지만 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결코 사용되지 않는 것은 한 번만 구성되며 최적화에 영향을 미치지 않습니다.

따라서,이 Romeocuddle Juliet만 그 때문에 단지 그는 구성하고 액세스 할 수 있습니다 Romeo.Love에 필요한 인스턴스 Julietcuddle그녀 (또는 다른 그녀가 당신을 때릴 것을 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 , 23 .


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.