web-dev-qa-db-ja.com

このクラスはなぜ変更可能ですか?

public class Test {
    private final String url;
    public Test(String url) {
        this.url = url;
    }
    public String getUrl() {
        return url;
    }
}

Testクラスには以下があります。

  1. プライベートで最終的なインスタンス変数は1つだけです。
  2. セッターなし。
  3. インスタンス変数を初期化する唯一の方法は、コンストラクターを使用することです。
  4. また、URLが設定されると、そのメソッドがTestのサブクラスによってオーバーライドされていても、getUrlでさえ変更できません。

しかし、私が読んでいる本は、上記のTestクラスが変更可能であると言っています:

  • どちらのクラスもfinalではないため、拡張でき、サブクラスはインスタンスメソッドをオーバーライドできます。ただし、Testクラスには、コンストラクタ以外のインスタンスメソッドはありません。

  • コンストラクタもプライベートではありません。

Testクラスが変更可能である理由を理解するのを手伝ってくれませんか?

41
Ram

Testの直接のインスタンスは不変ですが、Testの任意のインスタンスは不変であることが保証されていません。しかし、このサブクラスを考えてみましょう:

public class MutableTest extends Test {
        private int mutable;
        public MutableTest(String url) {
                super(url);
        }

        @Override
        public String getUrl() {
                return super.getUrl() + mutable++;
        }
}

次に、次のようなものを書くことができます:

Test instance = new MutableTest("http://example.com/");
String firstGet = instance.getUrl();
String secondGet = instance.getUrl();
assertEquals(firstGet, secondGet); // Boom!
58
gustafc

タイプTestであることがわかっているオブジェクトを受け取るオブジェクトは、そのオブジェクトが不変であることを知っています。ただし、Test型のnull以外の参照を受け取るオブジェクトには、言語によって定義された方法で、識別されたオブジェクトが変更可能でないことを確認する方法はありません。一部の人々はこれを主要な心配と見なしているようです。しかし、一般的にはそうあるべきではないと思います。

クラスが有効に継承可能である場合、一般に、意図されていない目的にはまったく適さない派生型を簡単に作成できます。言語がそれを防止しようとする唯一の方法は、派生型が実行できる有用なことの範囲を大幅に制限することです。ただし、いくつかの特殊なクラス(通常はセキュリティに関連するクラス)以外では、一般に、派生クラスが実用的で役に立たないことを行う可能性について心配するよりも、派生クラスが役立つことを保証する方が適切です。

0
supercat