12

Javaでは、非抽象クラスの拡張を許可するケースはありますか?

クラス階層がある場合、常に悪いコードを示しているようです。同意しますか、なぜ/しないのですか?

4

10 に答える 10

12

最終的ではない具体的なクラスを持つことが理にかなっている場合は確かにあります。ただし、Kent の意見には同意します。クラスはデフォルトで final (C# では封印) である必要があり、Java メソッドはデフォルトで final (C# の場合と同様) である必要があると考えています。

Kent が言うように、継承には慎重な設計と文書化が必要です。1 つのメソッドをオーバーライドするだけでよいと考えるのは非常に簡単ですが、そのメソッドが実装の残りの部分として基本クラスから呼び出される可能性がある状況はわかりません。

詳細については、「継承用のクラスをどのように設計しますか」を参照してください。

于 2009-02-06T11:22:34.410 に答える
11

私は Jon と Kent に同意しますが、Scott Myers (Effective C++ で) のように、私はさらに先に進みます。私は、すべてのクラスが、または のいずれかであるべきだabstractfinalと信じています。つまり、直接インスタンス化に適しているのは、任意の階層のリーフ クラスだけです。他のすべてのクラス (つまり、継承内の内部ノード) は「未完成」であり、したがってabstract.

通常のクラスをさらに拡張しても意味がありません。クラスの側面が拡張および/または変更する価値がある場合、よりクリーンな方法は、その 1 つのクラスを取得し、それを 1 つのabstract基本クラスと 1 つの具体的な交換可能な実装に分離することです。

于 2009-02-06T11:41:39.753 に答える
5

この質問は、C# .NET などの他のプラットフォームにも同様に当てはまります。タイプはデフォルトで final/sealed である必要があり、継承を許可するには明示的に unsealed する必要があると信じている人 (私自身を含む) があります。

継承による拡張は、慎重な設計が必要なものであり、型を封印しないままにしておくほど単純ではありません。したがって、継承を許可することは明示的な決定であるべきだと思います。

于 2009-02-06T11:17:00.037 に答える
4

ここで最も参考になるのは、Joshua Bloch の優れた本「Effective Java」の項目 15 で、「継承または禁止のための設計とドキュメント」と呼ばれています。ただし、クラスの拡張を許可するかどうかの鍵は、「抽象的か」ではなく、「継承を考慮して設計されたか」です。2 つの間に相関関係がある場合もありますが、重要なのは 2 番目です。簡単な例を挙げると、ほとんどの AWT クラスは、たとえ抽象的でないクラスであっても、拡張できるように設計されています。

ブロッホの章の要約は、祖先が継承されるように設計されていない場合、継承されたクラスとその親との相互作用は驚くべきものであり、予測不可能になる可能性があるということです。したがって、クラスには 2 種類のクラスを用意する必要があります。a) 拡張できるように設計されたクラスと、拡張方法を説明する十分なドキュメントを備えたクラス。b) final とマークされたクラス。(a) のクラスは抽象的であることが多いですが、常にそうとは限りません。為に

于 2009-02-06T15:15:54.597 に答える
3

同意しません。階層構造が悪ければ、オブジェクト指向言語が存在する理由はありません。Microsoft と Sun の UI ウィジェット ライブラリを見れば、継承が確実に見つかります。定義上、それはすべて「悪いコード」ですか?いいえ、もちろん違います。

継承は悪用される可能性がありますが、どの言語機能も悪用される可能性があります。秘訣は、物事を適切に行う方法を学ぶことです。

于 2009-02-06T11:21:06.113 に答える
3

場合によっては、サブクラス化がないことを確認したい場合もあれば、サブクラス化 (抽象) を確実にしたい場合もあります。しかし、元の作成者としてのあなたが気にしない、そして気にするべきではないクラスの大きなサブセットが常に存在します。開閉の一部です。何かを閉鎖する必要があると判断すること、理由があって行う必要があります。

于 2009-02-06T11:21:49.013 に答える
2

私はこれ以上同意できませんでした。具象クラスが final とマークしていないメソッドの可能な戻り値の型を知っている場合、クラス階層は具象クラスにとって意味があります。たとえば、具象クラスにはサブクラス フックがある場合があります。

protected SomeType doSomething() {
return null;
}

この doSomething は、null または SomeType インスタンスであることが保証されています。SomeType インスタンスを処理する機能はあるが、現在のクラスで SomeType インスタンスを使用するためのユース ケースがないとします。ただし、この機能はサブクラスにあると非常に優れており、ほとんどすべてが具体的であることがわかっています。null 値で何もしないというデフォルトで直接使用できる場合、現在のクラスを抽象クラスにしても意味がありません。抽象クラスにした場合、その子は次のタイプの階層になります。

  • 抽象基本クラス
    • 既定のクラス (非抽象である可能性があり、保護されたメソッドのみを実装し、他には何も実装しないクラス)
    • その他のサブクラス。

したがって、デフォルト クラスが最も一般的なケースである場合、直接使用できない抽象基本クラスがあります。もう 1 つの階層では、クラスが 1 つ少ないため、本質的に役に立たないデフォルト クラスを作成することなく機能を使用できます。これは、クラスに抽象化を強制するだけで済むためです。

  • デフォルトのクラス
    • その他のサブクラス。

確かに、階層は使用および悪用される可能性があり、物事が明確に文書化されていない場合、またはクラスが適切に設計されていない場合、サブクラスで問題が発生する可能性があります。しかし、これらと同じ問題が抽象クラスにも存在します。クラスに「抽象」を追加したからといって、問題を取り除くことはできません。たとえば、上記の「doSomething()」メソッドのコントラクトで、getter および setter を介してアクセスされたときに SomeType に x、y、および z フィールドを設定する必要がある場合、null を返す具象クラスを使用したかどうかに関係なく、サブクラスは爆発します。基本クラスまたは抽象クラスとして。

クラス階層を設計するための一般的な経験則は、かなり単純な質問です。

  1. サブクラスで提案されたスーパークラスの動作が必要ですか? (Y/N) これは、自問する必要がある最初の質問です。動作が必要ない場合は、サブクラス化の引数はありません。

  2. サブクラスで提案されたスーパークラスの状態が必要ですか? (Y/N) これは 2 番目の質問です。状態が必要なもののモデルに適合する場合、これはサブクラス化の候補になる可能性があります。

  3. サブクラスが提案されたスーパークラスから作成された場合、それは本当に IS-A 関係になるのでしょうか?それとも動作と状態を継承するための単なる近道でしょうか? これが最後の質問です。それが単なるショートカットであり、提案されたサブクラスを「スーパークラスとして」修飾できない場合は、継承を避ける必要があります。状態とロジックをコピーして別のルートを持つ新しいクラスに貼り付けるか、委任を使用できます。

クラスが動作、状態を必要とし、スーパークラスのサブクラス IS-A(n) インスタンスがスーパークラスから継承されたと見なされるべきであると見なされる場合にのみ。それ以外の場合は、目的により適した他のオプションが存在しますが、前もってもう少し作業が必要になる場合がありますが、長期的にはよりクリーンです。

于 2009-02-06T16:38:51.573 に答える
1

動作の変更を許可したくない場合がいくつかあります。たとえば、String クラス、Math.

于 2009-02-06T11:16:23.720 に答える
1

同じことを行うためのより良い方法が常にあるため、継承は好きではありませんが、巨大なシステムでメンテナンスの変更を行っている場合、最小限の変更でコードを修正する最善の方法は、クラスを少し拡張することです。はい、通常は悪いコードにつながりますが、最初に何ヶ月も書き直すことなく、機能するコードになります。そのため、メンテナンス担当者にできる限りの柔軟性を与えることは、良い方法です。

于 2009-02-06T11:21:49.373 に答える