Java名隠し:難しい方法


96

解決するのが非常に難しい名前の非表示に問題があります。これは問題を説明する簡単なバージョンです:

クラスがあります: 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では許可されていません。


いくつかのクイックフィックスはこれである可能性があります:public void help(net.foo.X x) { x.doSomething(); }そして次のように呼び出しますhelp(null);
Absurd-Mind

@ Absurd-Mind:まあ、存在しないオブジェクトを介して静的メソッドを呼び出すことは私の解決策よりも厄介なようです:)。コンパイラの警告も表示されます。しかし、真実は、これは私のものに加えて別のハックなソリューションになるでしょう。
2014

@JamesB:これは間違ったXをインスタンス化します!net.foo.Xメソッドではなく、org.A.X
2014

3
から継承する必要がありAますか?ご存知のように、継承は非常に厄介なものになる可能性があります...
ドナルフェロー

2
I could call another class that in turn calls X.doSomething(); but this class would only exist because of the name clash, which seems very messyクリーンなコードの態度のための+1。しかし、私にとっては、これはトレードオフを行うべき状況のようです。単にこれを実行し、なぜそれを実行しなければならなかったのかについての長いコメントを投げてください(おそらくこの質問へのリンクが付いています)。
sampathsris 2014

回答:


84

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

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

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

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

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

@SuppressWarnings("static-access")

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


1
何故なの?あなたは正しいことをし、コードをリファクタリングするだけです。
ギンビー2014

1
静的インポートはこのソリューションほど明確ではなく、すべてのケースで機能するわけではありません(doSomething階層内にメソッドがある場合は失敗します)ので、最善のソリューションです。
Voo

@Voo私が実際に行って、他の人が回答としてリストしたすべてのオプションを試してみたことを自由に認め、最初に元の問題を正確に再現しました。元の質問は、他のすべてのより良いオプションをきちんと閉じています。
ドナルフェロー

1
創造性に賛成票を投じてください、しかし、私はこれをプロダクションコードでは決してしません。間接参照がこの問題の答えだと思います(すべての問題に対してではないですか?)。
ethanfar、2014

このソリューションを提供してくれたDonalに感謝します。実際、このソリューションは、「タイプ」を使用でき、変数を使用できない場所を強調しています。したがって、「キャスト」では、同じ名前の変数があったとしても、コンパイラは「タイプ」を選択します。このような興味深いケースについては、「Javaの落とし穴」の本を2冊お勧めします。books.google.co.in/ books / about /…books.google.co.in
RRM

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をご覧ください


おそらく最もクリーンなソリューションが提供されます。もし私がその問題を抱えていたら、私はおそらくそれを使うでしょう。
LordOfThePigs 2014

37

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

import static net.foo.X.doSomething;

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

ということに注意してBA名前付きのメソッドを含めないでくださいdoSomething


net.foo.X.doSomethingにはパッケージへのアクセスしかありませんか?パッケージcom.barからそれにアクセスできないことを意味します
JamesB

2
@JamesBはい。ただし、メソッドにアクセスできないため、完全な質問には意味がありません。例を簡略化している間、これはエラーだと思います
Absurd-Mind

1
しかし、元の質問は積極的にdoSomethingメソッドの名前として使用したいようBです…
ドナルフェロー14

3
@DonalFellows:あなたは正しいです。私が投稿したコードをそのまま残さなければならない場合、このソリューションは機能しません。幸い、メソッドの名前を変更できます。ただし、自分のメソッドが別のメソッドをオーバーライドし、名前を変更できない場合を考えてください。この場合、この答えは実際には問題を解決しません。しかし、これは偶然にも私の問題よりもさらに偶然であるため、トリプルネームの衝突でこのような問題に遭遇する人間は決していないでしょう:)
2014

4
他のすべての人に明確にするために:このdoSomethingメソッドは、継承階層にどこかにあるとすぐには機能しません(メソッド呼び出しターゲットがどのように解決されるかによります)。したがって、toStringメソッドを呼び出す場合は、Knuthが役立ちます。Donalによって提案されたソリューションは、BlochのJava Puzzlersの本にあるものです(私はそれを調べていません)、それで私たちはそれを本当に信頼できる答えと見なすことができます:-)
Voo

16

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

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

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

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

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

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


2
この答えは、これまでで最大のやり過ぎです
いいね

3
この回答には補完主義のバッジが必要です。あなたは、Javaの古いバージョンを使用してこの正確な問題を解決しなければならない、その貧しい貧しい人に答えを提供しました。
ギンビー2014

実際にstatic import機能が追加されただけのことは考えていませんでしたJava 1.5。1.4以下で開発する必要がある人はうらやましくはありません。一度しか作成する必要がなく、ひどいものでした。
EpicPandaForce 14

1
@Gimby:さて、((net.foo.X)null).doSomething()古いjavaで動作する解決策はまだあります。タイプはならAでもインナータイプを含むnetTHENこれは有効な:)残る唯一の答えです。
殺虫剤2014

6

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

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

3
null型にキャストしてからその型でメソッドを呼び出すと、オブジェクトの作成に不要な副作用が生じる可能性があるため、より適切です。
2014

@gexicideキャストのアイデアは、それnullを行うためのよりクリーンな方法の1つであるように思われます。@SuppressWarnings("static-access")警告なしである必要があります...
ドナルフェロー

についての正確さをありがとうnull、しかしnull私にとってキャスティングは常に心を砕くものでした。
Michael Laffargue、2014

4

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

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

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

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

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

    public class B extends A {

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

興味深い、さらに別のアプローチ。ただし、静的メソッドを持つクラスは、最後のユーティリティクラスです。
2014

ええ、その場合、このトリックは使用できません。静的メソッドが言語であるもう1つの理由は失敗し、Scalaのオブジェクトアプローチを採用する必要があります。
billc.cn 2014

とにかく別のクラスを作成する場合は、blgtの回答に記載されているデリゲートクラスを使用するのがおそらく最善です。
LordOfThePigs 2014

@LordOfThePigsこれにより、余分なメソッド呼び出しや将来のリファクタリングの負担を回避できます。新しいクラスは基本的にエイリアスとして機能します。
billc.cn 2014

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...

         }
     }

これはどのように作動しますか?AFAIK getGlobal()はパッケージの標準Javaメソッドではありません...(パッケージにJavaのメソッドを含めることはできないと思います...)
siegi

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

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

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


クラス宣言implements SomethingStrategyにを追加するのを忘れましたXSomethingStrategy
Ricardo Souza

@rcdmkありがとうございます。今すぐ修正する必要があります。
Erik Madsen
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.