Java でのアップキャストはサブクラスのメソッドとフィールドを非表示にしますか?

StackOverflow https://stackoverflow.com/questions/712528

  •  23-08-2019
  •  | 
  •  

質問

私が書いているプログラムにはクラスがあります RestrictedUser そしてクラス User それはから派生した RestrictedUser. にキャストしてユーザー固有のメソッドを非表示にしようとしています RestrictedUser ただし、キャストを行うときは、User メソッドがまだ使用可能です。また、デバッガーを実行すると、変数の型が次のように表示されます。 User.

RestrictedUser restricted = regularUser;

Java でのアップキャストはサブクラスのメソッドとフィールドを隠しますか、それとも何か間違ったことをしているのでしょうか?回避策はありますか?

ありがとう

役に立ちましたか?

解決

このコードを実行しようとした場合:

User user = new User(...);
RestrictedUser restricted = user;
restricted.methodDefinedInTheUserClass(); // <--

あなたはコンパイルエラーになるだろう。あなたが別の方法、たとえば、にRestrictedUserの周囲を通過したとしても、その方法がこれを行うことができますので、それは、どのような方法、形状、またはフォームでセキュリティで保護されません。

if (restricted instanceof User) {
    User realUser = (User)restricted;
    realUser.methodDefinedInTheUserClass(); // !!
}

と、あなたの「制限」ユーザーがそのようにもう制限されない。

それは、オブジェクト参照がUser変数に格納されている場合でも、Userオブジェクトとしてあるため、

オブジェクトがRestrictedUserオブジェクトとしてデバッガ中に表示されています。基本的にはなく、安全に、親クラスの型の変数に子クラスのインスタンスを実際に「隠す」、サブクラスのメソッドとフィールドます入れます。

他のヒント

私はあなたが使用している用語は少し不明であるが、ここで行くように、あなたが求めている正確に何かわかりません。あなたはスーパークラスとサブクラスを持っている場合は、それらを民間することによって、サブクラスからスーパークラスのメソッドを非表示にすることができます。あなたが見えないように、スーパークラスのパブリックメソッドが必要な場合は、運の外にあります。あなたはスーパークラスにサブクラスをキャストする場合は、サブクラスのパブリックメソッドは表示されなくなります。

あなたは、むしろ、継承よりも組成物を用いる別々のクラスに感度の高い方法を抽出し、必要に応じてオブジェクトにそのクラスの適切なインスタンスを挿入することであろう役立つかもしれない1つの提案。あなたがする必要がある場合には、注入されたクラスにクラスからメソッドを委任することができます。

あなたは静的な型と動的な型を混乱しています。

静的タイプは、参照のタイプです。あなたはアップキャストベースに派生から、あなたは限りすると、それは知っているように、事は指摘コンパイラを言っているときに基本です。それはあなたがオブジェクトがnull、または塩基から誘導された基、または何かのいずれかであるために指摘することを約束している、です。言うことである、それはベースのパブリックインターフェイスを備えています。

あなたは、その後(とないベースで)派生で宣言されたメソッドを呼び出すことはできませんが、あなたが派生によって上書き任意の基本メソッドを呼び出すとき、あなたはオーバーライドされたバージョンは、の派生取得します。

あなたはサブクラスを保護する必要がある場合は、委任パターンを使用することができます:

public class Protect extends Superclass {  // better implement an interface here
  private final Subclass subclass;

  public Protect(Subclass subclass) {
    this.subclass = subclass;
  }

  public void openMethod1(args) {
    this.subclass.openMethod1(args);
  }

  public int openMethod2(args) {
    return this.subclass.openMethod2(args);
  }
  ...
}

また、 Javaを使用して考えることができます.lang.reflect.Proxy
[]

ピーターの答え そして ジョンの答え は正しい:オブジェクトの実際の型 (クライアント コードがキャストできる、リフレクション メソッドを呼び出すことができるなど) がまだ利用可能な場合、静的型をキャストするだけでは何も行われません。(Peter の回答で述べたように) 何らかの方法で実際の型をマスクする必要があります。

実際にはこれを行うパターンがあります。を見てください unconfigurable* のメソッド Executors JDK ソース コードにアクセスできる場合は、クラスを参照してください。

ジャワ

Java では、オブジェクトから何かを「隠す」ことはできません。コンパイラは、特定のインスタンスについて詳細に知っていることを忘れてしまう可能性があります。(サブクラスが何であるかなど) デバッガーはコンパイラーよりも多くのことを知っているため、現在のインスタンスが実際にどのような型であるかを教えてくれます。おそらく、それがあなたが経験していることです。

OOP

使用しているオブジェクトの種類を知る必要があるロジックを作成しているようですが、これは OOP では推奨されていませんが、実用的な理由から必要になることがよくあります。

形容詞の制限

質問へのコメントで述べたように、ここでの基本クラスが何であるかを明確にする必要があります。直感的には、RestrictedUser は、名前がより特殊なタイプを示唆しているため、User のサブクラスである必要があります。(名前に追加の Active 形容詞があります。) あなたの場合、これは特別です。これは、User を RestrictedUser のサブクラスとしてまったく問題なく配置できる制限形容詞であるためです。2 つの名前を次のように変更することをお勧めします。BasicUser/UserWithId と NonRestrictedUser は、ここで混乱を招く部分である制限形容詞を避けるためのものです。

また、あなたがいずれかの匿名クラスのメソッドを隠すことができることを考えるように誘惑することはありません。例えば、これは、あなたが期待するのプライバシーを提供することはありません。

Runnable run = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello, world!");
    }

    public void secretSquirrel() {
        System.out.println("Surprise!");
    }
}

あなたは(直接にキャストするために)runの実数型に名前を付けることができないかもしれませんが、あなたはまだgetClass()でそのクラスを取得することができ、あなたはリフレクションを使用してsecretSquirrelを呼び出すことができます。 (secretSquirrelがプライベートである場合でも、あなたは何SecurityManagerを持っていない場合、またはアクセシビリティを許可するように設定されていた場合、反射があまりにもプライベートメソッドを呼び出すことができます。)

私は、あなたはおそらく貧しい命名選択をしたと思う:私はRestrictedUserUserのサブクラスではなく、他の方法で回避と思うだろうので、私はサブクラスとしてUnrestrictedUserを使用します。

あなたはUnrestrictedUserRestrictedUserをアップキャストした場合、

、あなたの参照のみがRestrictedUserで宣言されたメソッドにアクセスすることができるようになります。あなたが戻っUnrestrictedUserにそれをダウンキャストしかし、もし、あなたはすべてのメソッドにアクセスすることができます。 Javaオブジェクトは、彼らが実際にあるどのような種類を知っているので、実際にUnrestrictedUserあるものは、常に関係なく、あなたが(でもObject)を使用している参照の種類、それにキャストバックすることはできません。やむを得ないと言語の部分があります。ただし、プロキシのいくつかの並べ替えの背後にある、おそらく目的に反し、あなたのケースでそれを隠すことができます。

また、Javaで、すべての非静的メソッドはデフォルトで仮想的です。これは、あなたがUnrestrictedUser参照を介してアクセスしても、RestrictedUserUnrestrictedUserで宣言されたいくつかのメソッドをオーバーライドした場合、その後、RestrictedUserバージョンが使用されることを意味します。あなたは親クラスのバージョンを起動する必要がある場合は、RestrictedUser.variableName.someMethod()を呼び出すことによって、非仮想呼び出しを行うことができます。しかし、あなたはおそらく(それがカプセル化を壊すということで理由の少なくとも)非常に多くの場合、これを使うべきではありません。

また、私はこの質問には別の質問を頼むと思います。どのようにこのメソッドをコールする予定ですか?これは、サブクラス化が発信者でinstanceofチェックが必要になります新しいメソッドのシグネチャを紹介するクラス階層構造を持つように思えます。

あなたが問題になっているこれらのメソッドを呼ぶようなシナリオが含まれるようにあなたの質問を更新することはできますか?おそらく、我々はより多くのを助けることができるようになります。

技術的な答えはジョンCalsbeekによって与えられています。私は、設計上の問題について考えたいです。あなたがやろうとしていることは、Userクラスについて何を知ってから、コードのいくつかのセグメント、または一部の開発者を防ぐことです。あなたは、彼らがRestrictedUsersであるかのように彼らはすべてのユーザーを扱いたい。

あなたがそれを行うような方法は、パーミッションです。あなたは、Userクラスへのアクセス権を持つ問題の開発を防ぐために必要です。おそらく、パッケージに置くと、パッケージへのアクセスを制限することでそれを行うことができます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top