8

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

RestrictedUser restricted = regularUser;

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

ありがとう

4

10 に答える 10

17

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

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

コンパイルエラーが発生します。RestrictedUserこれは、たとえば別のメソッドに渡されたとしても、そのメソッドはこれを行うことができるため、どのような方法、形状、または形式でも安全ではありません。

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

「制限された」ユーザーはそれほど制限されなくなりました。

オブジェクト参照が変数に格納されている場合でも、オブジェクトはオブジェクトでUserあるため、オブジェクトはデバッガーにオブジェクトとして表示されます。基本的に、子クラスのインスタンスを親クラスの型を持つ変数に入れると、実際にはサブクラスのメソッドとフィールドが「隠蔽」されますが、安全ではありません。UserRestrictedUser

于 2009-04-03T04:23:59.753 に答える
3

サブクラスを保護する必要がある場合は、デリゲート パターンを使用できます。

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
[]]を使用することも考えられます。

于 2009-04-03T08:58:14.633 に答える
3

あなたが使用している用語が少し不明確であるため、あなたが何を求めているのか正確にはわかりませんが、ここに行きます. スーパークラスとサブクラスがある場合、スーパークラスのメソッドをプライベートにすることでサブクラスから隠すことができます。スーパークラスの public メソッドを非表示にする必要がある場合は、運が悪いです。サブクラスをスーパークラスにキャストすると、サブクラスのパブリック メソッドは表示されなくなります。

継承ではなく構成を使用し、機密性の高いメソッドを別のクラスに抽出し、必要に応じてそのクラスの適切なインスタンスをオブジェクトに挿入することをお勧めします。必要に応じて、クラスから注入されたクラスにメソッドを委譲できます。

于 2009-04-03T04:23:32.597 に答える
3

静的型と動的型を混同しています。

静的タイプは参照のタイプです。Derived から Base にアップキャストすると、コンパイラーに、あなたとコンパイラーが知る限り、指しているものが Base であることを伝えていることになります。つまり、指しているオブジェクトがnull、Base、またはBaseから派生したものであることを約束しています。つまり、Base のパブリック インターフェイスを備えています。

その後、Derived で宣言された (Base ではなく) メソッドを呼び出すことはできなくなりますが、Derived によってオーバーライドされた Base メソッドを呼び出すと、Derived のオーバーライドされたバージョンが取得されます。

于 2009-04-03T04:24:14.153 に答える
2

Java

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

OOP

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

形容詞の制限

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

于 2009-04-03T12:24:37.400 に答える
2

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

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

于 2009-04-03T04:52:11.810 に答える
1

また、匿名クラスのメソッドも非表示にできると考えたくはありません。たとえば、これは期待するプライバシーを提供しません。

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

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

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

于 2009-04-03T05:00:12.787 に答える
0

技術的な回答は、John Calsbeek によって与えられました。設計上の問題について考えたいと思います。あなたがやろうとしているのは、コードの一部または一部の開発者が User クラスについて何も知らないようにすることです。すべてのユーザーを、RestrictedUsers であるかのように扱うようにします。

それを行う方法は、許可を使用することです。問題の開発者が User クラスにアクセスできないようにする必要があります。パッケージに配置して、パッケージへのアクセスを制限することで、おそらくそれを行うことができます。

于 2009-04-03T16:56:52.487 に答える
0

おそらく命名の選択が不適切だったと思います。私は のサブクラスになると思いますRestrictedUserUser、その逆ではないのでUnrestrictedUser、サブクラスとして使用します。

を にアップキャストするUnrestrictedUserRestrictedUser、参照は で宣言されたメソッドにしかアクセスできなくなりますRestrictedUser。ただし、これを にダウンキャストすると、UnrestrictedUserすべてのメソッドにアクセスできます。Java オブジェクトは実際の型を認識しているため、使用している参照の型 ( であっても) に関係なく、実際に であるすべてのものをUnrestrictedUser常にそれにキャストバックできますObject。言語のやむを得ない部分があります。ただし、ある種のプロキシの背後に隠すことはできますが、あなたの場合はおそらく目的に反します。

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

于 2009-04-03T12:56:26.653 に答える
0

また、この質問は別の質問を生むと思います。このメソッドをどのように呼び出す予定ですか? サブクラス化によって新しいメソッド シグネチャが導入されるクラス階層を持つと、呼び出し側で instanceof チェックが必要になるようです。

問題のこれらのメソッドを呼び出すシナリオを含めるように質問を更新できますか? おそらく、私たちはもっと助けることができるでしょう。

于 2009-04-03T13:20:12.290 に答える