97

解決するのが非常に難しい名前の隠蔽に問題があります。問題を説明する簡略化されたバージョンを次に示します。

クラスがあります: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.Xとクラスorg.Aはライブラリ クラスなので、変更できません。

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

global::C# や::C++など、グローバル名前空間を指定できる言語では、netこのグローバル プレフィックスを前に付けることもできますが、Java ではそれができません。

4

9 に答える 9

85

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

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

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

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

欠点は、このコードが本当にひどいことです! 私にとっては、警告が生成されますが、これは一般的には良いことです。しかし、そうでなければ完全に非現実的な問題を回避しているので、

@SuppressWarnings("static-access")

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

于 2014-07-04T13:14:24.723 に答える
38

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

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

于 2014-07-04T11:09:33.183 に答える
37

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

import static net.foo.X.doSomething;

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

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

于 2014-07-04T10:43:07.023 に答える
4

キャストを実行したり、奇妙な警告を抑制したり、冗長なインスタンスを作成したりする必要はありません。サブクラスを介して親クラスの静的メソッドを呼び出すことができるという事実を利用した単なるトリックです。(ここでの私のハックな解決策に似ています。)

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

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

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

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

    public class B extends A {

        public void doSomething() {
            XX.doSomething();
        }
于 2014-07-05T17:24:45.293 に答える
2

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

         }
     }
于 2014-07-08T08:30:44.723 に答える
1

これが、合成が継承よりも好ましい理由の 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();
    }
}
于 2014-07-04T16:36:34.547 に答える
0

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

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

これで、依存関係も切り離されたので、単体テストが書きやすくなります。

于 2014-07-15T11:02:58.207 に答える