11

私は次のクラスを持っています

Hello.java

package speak.hello;

import java.util.Map;

import speak.hi.CustomMap;
import speak.hi.Hi;

public class Hello {

    private Hi hi;

    Hello(Hi hi) {
        this.hi = hi;
    }

    public String sayHello() {
        return "Hello";
    }

    public String sayHi() {
        return hi.sayHi();
    }

    public Map<String, Object> getMap() {
        return hi.getMap();
    }

    public void clearMap() {
        hi.getMap().clear();
    }

    public void discardMap() {
        CustomMap map = (CustomMap) hi.getMap();
        map.discard();
    }

    public static void main(String[] args) {
        Hello hello = new Hello(new Hi());
        System.out.println(hello.sayHello());
        System.out.println(hello.sayHi());
        System.out.println(hello.getMap());
        hello.clearMap();
        System.out.println("--");
        hello.discardMap();
    }

}

Hi.java

package speak.hi;

import java.util.HashMap;
import java.util.Map;

public class Hi {
    public String sayHi() {
        return "Hi";
    }

    public Map<String, Object> getMap() {
        return new CustomMap<String, Object>();
    }
}

CustomMap.java

package speak.hi;

import java.util.HashMap;

public class CustomMap<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = -7979398843650044928L;

    public void discard() {
        System.out.println("Discarding Map");
        this.clearCache();
        this.clear();
    }

    @Override
    public void clear() {
        System.out.println("Clearing Map");
        super.clear();
    }

    private void clearCache() {
        System.out.println("Clearing Map");
    }
}

publicアクセス指定子をから削除するまで、これは正常に機能しますCustomMap

package speak.hi;

import java.util.HashMap;

class CustomMap<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = -7979398843650044928L;

    public void discard() {
        System.out.println("Discarding Map");
        this.clearCache();
        this.clear();
    }

    @Override
    public void clear() {
        System.out.println("Clearing Map");
        super.clear();
    }

    private void clearCache() {
        System.out.println("Clearing Map");
    }
}

コンパイラはそれを叫ぶ

タイプspeak.hi.CustomMapは表示されませ

変更するオプションがない場合(サードパーティのjarなど) 、speak.hi.CustomMapまだ使用できる方法はありますか?CustomMapspeak.hello.Hello


私が知っているオプションの1つは、Now Helloがパッケージに含まれているため、パッケージのプライベートクラスにアクセスできるようにすることですspeak.hello.Hellospeak.hi.Hellospeak.hiHi


これを行う他の方法はありますか?おそらく反射を使用していますか?


編集:@StephenCの要求に応じて追加の詳細で更新

4

10 に答える 10

12

これを行う他の方法はありますか?おそらく反射を使用していますか?

はい。アプリケーションに完全な権限がある場合は、リフレクションを使用してJavaアクセスルールをバイパスできます。

たとえばprivate、別のクラスのオブジェクトのフィールドにアクセスするには、次のことを行う必要があります。

  • オブジェクトのClassオブジェクトを取得します。
  • Class.getDeclaredField(...)このメソッドを使用しFieldて、フィールドのオブジェクトを取得します。
  • Field.setAccessible(true)アクセスチェックをオフにするために呼び出します。
  • Class.getField(object, Field)フィールドの値(またはプリミティブ型の場合はボックス化された値)を取得するために呼び出します。

クラス自体にアクセスできない場合は、ソースコードでクラス識別子を参照しないようにする必要があります...'cosは、コンパイルエラーになります。代わりに、その参照をタイプまたはその他の表示可能なスーパータイプの(たとえば)変数に割り当てObject、インスタンスに対してより具体的な操作を反射的に実行します。


ご想像のとおり、これは面倒でエラーが発生しやすくなります。次のような、より良い方法を見つけることをお勧めします。

  • クラスのサプライヤーに、視界の制限を破る必要がある原因を修正してもらう、
  • クラスのサプライヤに可視性を変更させる、
  • 抽象化を解き放つ必要のないクラスを使用する別の方法を見つける、または
  • それらを捨てて、より良いものを見つける(または書く)。

(一般的に言って、抽象化を壊さなければならない場合は、抽象化自体またはそれを使用する方法のいずれかに問題があります。)


最後に、信頼できないコードは、キーリフレクティブ操作の使用をブロックするセキュリティサンドボックスで実行される(すべきである)ことを追加する必要があります。

于 2011-11-25T11:13:54.370 に答える
7

default次のメソッドリフレクションを使用してスコープクラスメソッドを呼び出します

public void discardMap() {
    //CustomMap map = (CustomMap) hi.getMap();
    //map.discard();
    try {
        Object o =hi.getClass().getMethod("getMap").invoke(hi);
        Method m = o.getClass().getMethod("discard");
        m.setAccessible(true);
        m.invoke(o);
    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}
于 2012-01-17T15:34:44.113 に答える
3

次の理由で不可能な場合があります。

クラス :

同じパッケージからクラスにアクセスできますか?

  • パブリック:はい
  • 保護:はい
  • デフォルト:はい
  • プライベート:いいえ

別のパッケージからクラスにアクセスできますか?

  • パブリック:はい
  • 保護:いいえ
  • デフォルト:サブクラスでない限り
  • プライベート:いいえ
于 2011-11-25T11:03:17.063 に答える
1

完全を期すためにこのソリューションを追加します。

私が知っているオプションの1つは、speak.hello.Helloをspeak.hi.Helloに移動することです。NowHelloはパッケージspeak.hiに含まれているため、パッケージプライベートクラスHiにアクセスできます。

package speak.hi;

public class Hello {

    private Hi hi;

    Hello(Hi hi) {
        this.hi = hi;
    }

    public String sayHello() {
        return "Hello";
    }

    public String sayHi() {
        return hi.sayHi();
    }

    public static void main(String[] args) {
        Hello hello = new Hello(new Hi());
        System.out.println(hello.sayHello());
        System.out.println(hello.sayHi());
    }

}
于 2011-11-25T11:52:25.097 に答える
1

API以外のクラスは将来のバージョンで変更される可能性があり、コードが破損する可能性があるため、使用しないことをお勧めします。

このクラスをどうやって知りましたか?オープンソースライブラリですか?

ライブラリの作成者に連絡して、ユースケースを伝え、パブリックAPIを提供する方法を見つけてください。オープンソースライブラリの場合は、パッチを提供することで彼らを助けることができます。

于 2011-11-25T10:55:21.327 に答える
0

ありえない。セキュリティモデルは次のとおりです。セキュリティを提供するモデル:)クラスHiを設計し、プライベートアクセスを使用して顧客に配信した場合、制限を回避できないようにしますか?

于 2011-11-25T10:51:52.237 に答える
0

ライブラリの作成者が特定のクラスをパブリックAPIの一部にしない場合、それは他の人がそれを使用することを望まないためだと思います。リフレクションを使用して決定を破ることができるとしても、決定を尊重する必要があります。プライベートAPIの使用は、単に悪いプログラミングです。

于 2011-11-25T11:17:06.700 に答える
0

1番目のクラスはパッケージabcclass1にありますが、class1はプライベートであり、抽象です。2番目のクラスはパッケージにあります。abcclass2はclass1を拡張しますが、class2はパブリックです。

3番目のクラスはパッケージxyzclass3にあります

クラス3のclass1にアクセスするには、次のように記述できます。-

クラスbaseClass=(new class2())。getClass(); そして、そのスーパークラスのインスタンスを使用してから、次を使用します。-baseClass.getSuperClass(); 好きな場所で使用できます。

ただし、この場合も、基本クラスが抽象的でプライベートになっているため、そうすることはお勧めできませんが、このソリューションを回避策として使用することもできます。

于 2015-02-26T15:44:43.137 に答える
-1

デフォルトでは、クラスは「プライベート」ではなく「デフォルト」(パッケージプライベート、言うことができます)になると思います。そのため、同じパッケージでアクセスできます。

さらに、Javaで*トップレベルクラスをプライベートにすることはできません。

また、クラスをデフォルトにし、他のパッケージでそのクラスにアクセスできるようにしたい場合は、アクセス指定子(修飾子)を使用する目的は何ですか?

クラスを公開するか、同じパッケージに移動する必要があります。

于 2011-11-25T10:54:06.547 に答える
-2

不可能です。Hiクラスをプライベートとして作成することはできません。

于 2011-11-25T10:53:15.867 に答える