[java] 자바 이름 숨기기 : 어려운 길

해결하기 매우 어려운 이름 숨김 문제가 있습니다. 다음은 문제를 설명하는 단순화 된 버전입니다.

수업이 있습니다. org.A

package org;
public class A{
     public class X{...}
     ...
     protected int net;
}

그런 다음 수업이 있습니다 net.foo.X

package net.foo;
public class X{
     public static void doSomething();
}

이제 여기에서 상속 A하고 호출하려는 문제 클래스가 있습니다.net.foo.X.doSomething()

package com.bar;
class B extends A {

    public void doSomething(){
        net.foo.X.doSomething(); // doesn't work; package net is hidden by inherited field
        X.doSomething(); // doesn't work; type net.foo.X is hidden by inherited X
    }
}

보시다시피 이것은 불가능합니다. X상속 된 유형에 의해 숨겨져 있기 때문에 간단한 이름을 사용할 수 없습니다 . 나는 완전한 이름을 사용할 수 net.foo.X있기 때문에, net상속 된 필드에 의해 숨겨져 있습니다.

B내 코드베이스 에는 클래스 만 있습니다. 클래스 net.foo.Xorg.A라이브러리 클래스이므로 변경할 수 없습니다!

내 유일한 솔루션은 다음과 같습니다. 차례로 호출하는 다른 클래스를 호출 할 수 있습니다 X.doSomething(). 하지만이 클래스는 이름 충돌로 인해 존재할 수 있습니다. 내가 직접 호출 할 수있는 해결책이없는 X.doSomething()에서는 B.doSomething()?

예를 들어 global::C # 또는 ::C ++ 에서 전역 네임 스페이스를 지정할 수있는 언어에서는 net이 전역 접두사를 단순히 접두사 로 지정할 수 있지만 Java는이를 허용하지 않습니다.



답변

null형식으로 캐스트 한 다음 해당 메서드를 호출 할 수 있습니다 (대상 개체가 정적 메서드 호출에 관여하지 않기 때문에 작동 함).

((net.foo.X) null).doSomething();

이것은의 이점이 있습니다

  • 부작용이 없음 (인스턴스화 문제 net.foo.X)
  • 아무 것도 이름을 바꿀 필요가 없습니다 ( B원하는 이름으로 메서드를 지정할 수 있으므로 import static정확한 경우에는 작동하지 않습니다).
  • 델리게이트 클래스의 도입이 필요하지 않습니다 (좋은 생각이긴하지만…).
  • 리플렉션 API 작업의 오버 헤드 나 복잡성이 필요하지 않습니다.

단점은이 코드가 정말 끔찍하다는 것입니다! 저에게는 경고가 발생하며 일반적으로 좋은 일입니다. 그러나 완전히 비현실적인 문제를 해결하기 때문에

@SuppressWarnings("static-access")

적절한 (최소한!) 둘러싸는 지점에서 컴파일러가 종료됩니다.


답변

아마도 이것을 관리하는 가장 간단한 (반드시 가장 쉬운 것은 아님) 방법은 델리게이트 클래스를 사용하는 것입니다 :

import net.foo.X;
class C {
    static void doSomething() {
         X.doSomething();
    }
}

그리고 …

class B extends A {
    void doX(){
        C.doSomething();
    }
}

이것은 다소 장황하지만 매우 유연합니다. 원하는 방식으로 작동하도록 할 수 있습니다. 또한 static메서드와 인스턴스화 된 객체 모두에서 거의 동일한 방식으로 작동 합니다.

델리게이트 객체에 대한 자세한 내용은 http://en.wikipedia.org/wiki/Delegation_pattern을 참조 하십시오.


답변

정적 가져 오기를 사용할 수 있습니다.

import static net.foo.X.doSomething;

class B extends A {
    void doX(){
        doSomething();
    }
}

조심 그 BA이름이 방법을 포함하지 않는doSomething


답변

작업을 수행하는 적절한 방법은 정적 가져 오기이지만 절대 최악의 시나리오에서 정규화 된 이름을 알고 있으면 리플렉션을 사용하여 클래스의 인스턴스를 구성 할 수 있습니다.

Java : 기본 생성자가없는 클래스의 newInstance

그런 다음 인스턴스에서 메서드를 호출합니다.

또는 리플렉션을 사용하여 메서드 자체를
호출합니다. 리플렉션을 사용하여 정적 메서드 호출

Class<?> clazz = Class.forName("net.foo.X");
Method method = clazz.getMethod("doSomething");
Object o = method.invoke(null);

물론 이것들은 분명히 최후의 수단입니다.


답변

정답은 아니지만 X의 인스턴스를 만들고 이에 대한 정적 메서드를 호출 할 수 있습니다. 그것은 당신의 방법을 호출하는 방법 (내가 인정하는 더러운) 일 것입니다.

(new net.foo.X()).doSomething();


답변

캐스트를 수행하거나 이상한 경고를 억제하거나 중복 인스턴스를 만들 필요가 없습니다. 하위 클래스를 통해 부모 클래스 정적 메서드를 호출 할 수 있다는 사실을 사용하는 트릭입니다. ( 여기 내 hackish 솔루션과 유사합니다 .)

이런 클래스를 만드세요

public final class XX extends X {
    private XX(){
    }
}

(이 최종 클래스의 개인 생성자는 실수로이 클래스의 인스턴스를 만들 수 없도록합니다.)

그런 다음 자유롭게 전화 X.doSomething()를 걸 수 있습니다.

    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }


답변

모든 파일이 동일한 폴더에있는 경우 gobalnamespace를 얻으려고하면 어떨까요? ( http://www.beanshell.org/javadoc/bsh/class-use/NameSpace.html )

    package com.bar;
      class B extends A {

       public void doSomething(){
         com.bar.getGlobal().net.foo.X.doSomething(); // drill down from the top...

         }
     }