web-dev-qa-db-ja.com

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は継承されたフィールドによって隠されているため、完全修飾名_net.foo.X_を使用できません。

クラスBのみが私のコードベースにあります。クラス_net.foo.X_および_org.A_はライブラリクラスなので、変更できません。

私の唯一の解決策は次のようになります。X.doSomething()を呼び出す別のクラスを呼び出すことができます。しかし、このクラスは名前の衝突のためにのみ存在し、これは非常に厄介なようです! X.doSomething()からB.doSomething()を直接呼び出すことができる解決策はありませんか?

C#の_global::_またはC++の_::_などのグローバルネームスペースを指定できる言語では、netにこのグローバルプレフィックスをプレフィックスとして付けることができますが、Javaはそれを許可しません。

96
gexicide

nullを型にキャストし、その上でメソッドを呼び出すことができます(これは、ターゲットオブジェクトが静的メソッドの呼び出しに関与していないため機能します)。

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

これには次の利点があります

  • 副作用がない(net.foo.Xのインスタンス化に関する問題)、
  • 何も名前を変更する必要はありません(つまり、Bのメソッドに希望の名前を付けることができます。そのため、import staticは正確に機能しません)。
  • デリゲートクラスの導入を必要としない(それは良いアイデアかもしれませんが...)、そして
  • リフレクションAPIでの作業のオーバーヘッドや複雑さを必要としません。

欠点は、このコードが本当に恐ろしいことです!私にとって、それは警告を生成し、それは一般的に良いことです。しかし、他の方法では完全に実用的でない問題を回避しているため、

@SuppressWarnings("static-access")

適切な(最小!)囲みポイントでコンパイラーをシャットダウンします。

84
Donal Fellows

おそらくこれを管理する最も簡単な(必ずしも最も簡単ではない)方法は、デリゲートクラスを使用することです。

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

38
blgt

静的インポートを使用できます。

import static net.foo.X.doSomething;

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

BAにはdoSomethingという名前のメソッドが含まれていないことに注意してください

37
Absurd-Mind

物事の適切な方法は静的インポートですが、完全な最悪のシナリオでは、完全修飾名がわかっている場合は、リフレクションを使用してクラスのインスタンスを構築できます。

Java:デフォルトのコンストラクタを持たないクラスのnewInstance

次に、インスタンスでメソッドを呼び出します。

または、リフレクションを使用してメソッド自体を呼び出すだけです。 リフレクションを使用して静的メソッドを呼び出す

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

もちろん、これらは明らかに最後の手段です。

16
EpicPandaForce

実際には答えではありませんが、Xのインスタンスを作成して静的メソッドを呼び出すことができます。それはあなたのメソッドを呼び出す方法です(私が認める汚い)。

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

キャストを行ったり、奇妙な警告を抑制したり、冗長なインスタンスを作成したりする必要はありません。親クラスの静的メソッドをサブクラス経由で呼び出すことができるという事実を利用したトリックです。 (私のハックなソリューションと同様 here 。)

このようなクラスを作成するだけです

_public final class XX extends X {
    private XX(){
    }
}
_

(この最終クラスのプライベートコンストラクターは、このクラスのインスタンスが誤って作成されないようにします。)

次に、それを介してX.doSomething()を自由に呼び出すことができます。

_    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }
_
4
billc.cn

すべてのファイルが同じフォルダーにある場合、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...

         }
     }
2
Vectoria

これが、相続よりも構成の方が望ましい理由の1つです。

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

私は戦略パターンを使用します。

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

これで依存関係も分離されたので、単体テストを簡単に記述できます。

0
Erik Madsen