3

「元のデータへの不変のインターフェイスを返します。その後、オブジェクトのフィールドを変更できますが、呼び出し元はキャストして不正行為をしない限り変更できません。ユーザーに持たせたいメソッドのみを公開します。クラスで同じことを行うのは難しいため、サブクラスはそのスーパークラスが行うすべてを公開する必要があります」

彼はあなたがカンニングできるとはどういう意味ですか? また、なぜサブクラスでは難しいのですか?

ソース: http://mindprod.com/jgloss/immutable.html

4

3 に答える 3

8

interfaceミューテーションメソッドを持たないを提供します。次に、作成者だけが知っている変更可能な実装を提供します。

public interface Person
{
    String getName();
}

public class MutablePerson implements Person
{
    private String name;

    public MutablePerson(String name)
    {
        this.name = name;
    }

    @Override
    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }
}

この時点で、Personどこにでもオブジェクトを返す場合、誰かが返されたオブジェクトを変更する唯一の方法は、チートしてオブジェクトをにキャストバックすることMutablePersonです。事実上、コードが完全なハックでない限り、可変オブジェクトは不変になります。

Person person = new MutablePerson("picky");
// someone is cheating:
MutablePerson mutableAgain = (MutablePerson)person;
mutableAgain.setName("Phoenix");

// person.getName().equals("Phoenix") == true

真の実装が変更可能であることに気付く若いプログラマーの束を扱っていない場合、したがって彼らはそれを変更するためにキャストすることができます、そしてあなたは無限のコンストラクターなしでそれをまとめることができるという利点で不変性の安全性を提供します、またはビルダーを使用します(実際には、可変バージョンはビルダーです)。開発者が可変バージョンを悪用しないようにする良い方法は、可変バージョンをパッケージプライベートのままにして、パッケージだけがそれを認識できるようにすることです。その考えの欠点は、これが同じパッケージでインスタンス化される場合にのみ機能することです。これは事実かもしれませんが、DAOが複数のパッケージ定義の実装で使用される場合などの状況では明らかにそうではない可能性があります(例: 、MySQL、Oracle、Hibernate、Cassandraなど、すべて同じものを返します、

ここでの本当の鍵は、さらに下のインターフェースを実装する場合を除いて、Mutableオブジェクトから構築してはならないということです。拡張してから不変のサブクラスを返す場合、定義上、可変オブジェクトを公開する場合は不変ではありません。例えば:

public interface MyType<T>
{
    T getSomething();
}

public class MyTypeImpl<T> implements MyType<T>
{
    private T something;

    public MyTypeImpl(T something)
    {
        this.something = something;
    }

    @Override
    public T getSomething()
    {
        return something;
    }

    public void setSomething(T something)
    {
        this.something = something;
    }
}

public interface MyExtendedType<T> extends MyType<T>
{
    T getMore();
}

public class MyExtendedTypeImpl<T>
        extends MyTypeImpl<T>
        implements MyExtendedType<T>
{
    private T more;

    public MyExtendedTypeImpl(T something, T more)
    {
        super(something);

        this.more = more;
    }

    @Override
    public T getMore()
    {
        return more;
    }

    public void setMore(T more)
    {
        this.more = more;
    }
}

これは正直なところ、CollectionJavaのsが実装されるべき方法です。実装の代わりに読み取り専用インターフェースを使用できたCollections.unmodifiableため、不変バージョンの可変オブジェクトを予期せず使用することはありませんでした。つまり、不変性を隠すことはできませんが、可変性を隠すことはできます。

次に、本当に変更できない不変のインスタンスを振りかけることができ、それによって開発者は正直になります。同様に、上記のインターフェイスの不変バージョンがどこかに(より適切な名前で)表示されることを期待する可能性があります。

public class MyTypeImmutable<T> implements MyType<T>
{
    private final T something;

    public MyTypeImmutable(T something)
    {
        this.something = something;
    }

    @Override
    public T getSomething()
    {
        return something;
    }
}
于 2012-10-18T01:36:16.437 に答える
2

私はその声明はうまく表現されていないと思います、そして彼は単なる不変性以上のものに触れています(そして実際、その声明は本当に不変性でさえありません)。

特定のクラスではなく、データへのインターフェイスを返す場合、呼び出し元はインターフェイスでのみアクションを実行する必要があるという考え方です。したがって、インターフェイスにgetterメソッドしかない場合は、(ダウンキャストせずに)データを操作する方法はありません。

この階層を検討してください

interface AnInterface {
  void aGetter();
}

class MyMutableClass {
   void aGetter();
   void aSetter(...);
}

は可変ですMyMutableClassが、を返すことAnInterfaceにより、ユーザーはそれが実際に可変オブジェクトであることを知りません。したがって、オブジェクトは実際には変更可能ではありませんが、それを知るにはダウンキャストする(またはリフレクションを使用してミューテーターメソッドにアクセスする)必要があります。

今、あなたが持っていたとしましょう

class MyImmutableSubclass extends MyMutableClass {
   void anotherGetter();
}

サブクラスは「不変」ですが(親クラスは不変ではないため、実際にはそうではありません)、MyImmutableSubclassメソッドから戻った場合でも、呼び出し元はそれを公開してaSetterから呼び出すことができます。MyMutableClass

一般に、「リーク」状態を回避するために、不変オブジェクトを使用することをお勧めします。真に不変であるものはすべて、操作や意図しない変更から安全です。

于 2012-10-18T01:38:40.020 に答える
1

「何か」可変に型キャストすると、返されるフィールドのタイプを変更できるため、チートできます。パブリックインターフェイスの背後にクラスを「非表示」にして、その不変のインターフェイスを返す場合、ユーザーはインターフェイスをクラスに型キャストすることで不正行為を行うことができます。

クラスのプライベートメンバーはサブクラスに継承されませんが、保護され、パブリックに継承されるため、サブクラスを使用するのは難しいです。つまり、外部から親クラスでアクセスできるものはすべて、外部から子でもアクセスできるため、インターフェイスを使用する場合ほど簡単にユーザーを難読化することはできません。親メソッドをオーバーライドできるので、これは実際には可能だと思いますが、あまり意味がありません。

于 2012-10-18T01:44:55.040 に答える