継承(プログラミング)とは
あるクラスをベースとして、別のクラスを作成する方法です。
継承はオブジェクト指向プログラミングの基本の1つと呼ばれていますが、 継承はなるべく使わないようにすべきだと思います。 アンチパターンと言っても過言ではないかと思います。
安易な継承が引き起こす問題
他のクラスを継承できなくなる
Javaでは継承元は1つのため、あるクラスを継承すると、 別クラスの継承が行えなくなります。 そのため、必要のない継承は行わないようにすべきです。 C++のように多重継承がある言語では、「ダイヤモンド継承」という問題があります。
継承元、または継承先に不必要な制限が付く
Javaの継承の失敗例として、 Hashtableクラスを継承したjava.util.Properties、 Dateクラスを継承したjava.sql.Timestampがあります。 これらのクラスには、以下のような注意書きが入っています。
Propertiesクラスの場合
PropertiesはHashtableを継承するので、 Propertiesオブジェクトに対してputメソッドおよびputAllメソッドを適用できます。 しかし、これらのメソッドを使用することは推奨されません。 これらのメソッドを使うと、呼出し側はキーまたは値がStringsではないエントリを挿入できるからです。
Timestampクラスの場合
上記のようなTimestampクラスとjava.util.Dateクラスの違いのため Timestamp値はjava.util.Dateのインスタンスとして考えないでください。 Timestampとjava.util.Dateの継承関係は、型の継承ではなく、実装の継承を示します。
このような注意書きを必要とするのは、継承を行って拡張しているからです。 委譲を採用していれば、問題は起きませんでした。
継承を行う前に検討すべきもの
継承を使う前に、以下のものが使えないか検討すべきです。
委譲
継承のほとんどは、以下のように、 継承元クラスのオブジェクトをインスタンス変数として持つことで対応可能です。 この方式を委譲と呼びます。
public class Properties {
private Hashtable<String, String> hash;
public String setPrpperty(String key, String value) {
return this.hash.put(key, value);
}
public String getProperty(String key) {
return this.hash.get(key);
}
}
staticメソッド化
よく使われるメソッドを共通クラスに定義して、 サブクラスで使用する方法がありますが、 この多くはstatic importで対応可能です。
// 基底クラスにユーティリティメソッドを定義した例
public class Base {
protected boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
}
public class AClass extends Base {
public static void main(String[] args) {
System.out.println(isEmpty(args[0]);
}
}
上のような場合は、ユーティリティクラスを作成して、 static importを使用することで対応可能です。 また、Java 8から採用された、インターフェースのデフォルトメソッドでも可能です。
// staticメソッド化
pubic final class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.isEmpty();
}
}
// static importを使った呼び出し
import static StringUtils.*;
public class AClass {
public static void main(String[] args) {
System.out.println(isEmpty(args[0]);
}
}
ただし、ユーティリティクラスはオブジェクト指向としてよろしくないため、 既存のクラスの拡張としてのみ使うのが良いと思います。
Template Methodパターン
継承が必須であるパターンとして、Template Methodがあります。 しかし、注意して使用しないと、むしろ使いにくくなります。 Strategyパターンの使用を検討してください。