code

자바 이름 숨기기 : 어려운 길

codestyles 2020. 8. 25. 08:04
반응형

자바 이름 숨기기 : 어려운 길


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

수업이 있습니다. 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();
    }
}

This is somewhat verbose, but very flexible - you can get it to behave any way you want; plus it works in much the same way both with static methods and instantiated objects

More about delegate objects here: http://en.wikipedia.org/wiki/Delegation_pattern


You can use a static import:

import static net.foo.X.doSomething;

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

Watch out that B and A do not contain methods named doSomething


The proper way of doing things would be the static import, but in the absolute worst case scenario, you COULD construct an instance of the class using reflection if you know its fully qualified name.

Java: newInstance of class that has no default constructor

And then invoke the method on the instance.

Or, just invoke the method itself with reflection: Invoking a static method using reflection

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

Of course, these are obviously last resorts.


Not really THE answer but you could create an instance of X and call the static method on it. That would be a way (dirty I admit) to call your method.

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

There's no need to do any cast or suppress any strange warnings or create any redundant instance. Just a trick using the fact that you can call parent class static methods via the sub-class. (Similar to my hackish solution here.)

Just create a class like this

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

(The private constructor in this final class makes sure no one can accidentally create an instance of this class.)

Then you're free to call X.doSomething() via it:

    public class B extends A {

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

What if you try getting the gobalnamespace given all the files are in the same folder. (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...

         }
     }

This is one of the reasons that composition is preferable to inheritance.

package com.bar;
import java.util.concurrent.Callable;
public class C implements Callable<org.A>
{
    private class B extends org.A{
    public void doSomething(){
        C.this.doSomething();
    }
    }

    private void doSomething(){
    net.foo.X.doSomething();
    }

    public org.A call(){
    return new B();
    }
}

I would use the Strategy pattern.

public interface SomethingStrategy {

   void doSomething();
}

public class XSomethingStrategy implements SomethingStrategy {

    import net.foo.X;

    @Override
    void doSomething(){
        X.doSomething();
    }
}

class B extends A {

    private final SomethingStrategy strategy;

    public B(final SomethingStrategy strategy){
       this.strategy = strategy;
    }

    public void doSomething(){

        strategy.doSomething();
    }
}

Now you have also decoupled your dependency, so your unit tests will be easier to write.

참고URL : https://stackoverflow.com/questions/24572214/java-name-hiding-the-hard-way

반응형