第5章クラス拡張
第5章では、主にインターフェイス、抽象クラス、内部クラス、および列挙を紹介します。
インターフェースの性質
多くの場合、私たちが気にするのはオブジェクトのタイプではなく、オブジェクトの能力です。オブジェクトには、そのタイプに関係なく、特定の能力を提供する必要があります。インターフェイスは、オブジェクトが提供できる機能を説明するために使用されます。
インターフェイスは一連の機能を宣言しますが、この機能を単独で実装するのではなく、単なる規則です。インターフェースには、2者のオブジェクトの相互作用が含まれます。一方の当事者はこのインターフェースを実装する必要があり、もう一方の当事者はこのインターフェースを使用しますが、2つの当事者は相互に直接依存せず、インターフェースを介して間接的にのみ相互作用します。
インターフェイスの定義ではinterface
キーワードを使用します。次のように
public interface MyComparable {
int compareTo(Object other);
}
Java 8より前は、メソッドをインターフェイスに実装することはできません。また、インターフェイスメソッドに修飾子を追加する必要はなく、デフォルトはpublicabstractです。
クラスは、クラスのオブジェクトがインターフェースによって表される機能を持っていることを示すためにインターフェースを実装できます。implements
キーワードを使用してインターフェースを実装します。
public class Point implements MyComparable{
...
@Override
public int compareTo(Object other) {
...
}
}
インターフェイスを実装するには、クラス内のインターフェイスで定義されたメソッドをオーバーライドする必要があります。ジェネリックスを使用instanceof
する場合は、それを使用してオブジェクトのタイプをチェックし、不要なエラーを回避する必要があります。
クラスは複数のインターフェースを実装して、クラスのオブジェクトが複数の機能を持っていることを示すことができます。
public class Test implements interface1, interface2{
}
インターフェイスをnew
使用してインターフェイスオブジェクトを作成することはできません。インターフェイスを使用する唯一の方法は、クラスを介してインターフェイスを実装してから、このクラスのオブジェクトを作成し、オブジェクトのインターフェイスメソッドを呼び出すことです。インターフェイスを実装するオブジェクトは、親クラスと子クラスの関係と同様に、インターフェイスタイプの変数に割り当てることができます。
ここにインターフェースの力があります。オブジェクトのタイプ、つまりインターフェース指向プログラミングのアイデアがなくても、オブジェクトの機能を均一に考慮することができます。
さらに重要なことに、インターフェイスは結合を減らし、柔軟性を向上させます。インターフェイスを使用するコードは、インターフェイスの特定のタイプではなく、インターフェイス自体に依存します。プログラムは、分離されたユーザーに影響を与えることなく、状況に応じてインターフェイスの実装を置き換えることができます。
変数はインターフェースで定義できますが、変数タイプはpublic static final
public interface Interface1{
public static final int a = 0;
}
インターフェイスは継承できます。クラス継承とは異なり、インターフェイス継承は多重継承をサポートします。
public interface IBase1{
void method1();
}
public interface IBase2{
void method2();
}
public interface IChild extends IBase1, IBase2{
}
クラスの継承と実装インターフェイスは共存できます。
public class Child extends Base implements IChild{
}
インターフェイスは、instanceof
キーワードを使用して、オブジェクトが特定のインターフェイスを実装しているかどうかを判断することもできます。
Point p = new Point();
if( p instanceof MyComparable) {
}
インターフェイスとコンポジションを使用して、継承を効果的に置き換えることができます。
抽象クラス
抽象クラスは抽象クラスです。抽象化は具象に関連しています。一般的に、具象クラスには直接対応するオブジェクトがありますが、抽象クラスにはありません。グラフィッククラスShapeなどの抽象概念を表現します。抽象概念のメソッドは、一般に抽象メソッドとして定義されdraw
ます。たとえば、抽象クラスのメソッドは抽象メソッドです。抽象クラスはメソッドの実装方法を知らないため、サブクラス(Circleなどの具象クラス)のみがメソッドの実装方法を知っています。抽象クラスと抽象メソッドはどちらもabstract
キーワードを使用して宣言されます。
public abstract class Shape{
public abstract void draw();
}
抽象メソッドを定義するクラスは、抽象クラスとして宣言する必要がありますが、抽象クラスに抽象メソッドを含めることはできません。抽象クラスを使用new
してオブジェクトを作成することはできません。また、サブクラスを使用してオブジェクトを作成する必要があります。クラスが抽象クラスを継承した後、抽象クラスとしても宣言されていない限り、抽象クラスで定義されているすべての抽象メソッドを実装する必要があります。
public class Circle extends Shape{
@Override
public void draw(){
}
}
抽象クラスはオブジェクトを作成できませんが、抽象クラスの変数を宣言し、抽象クラスの具象サブクラスのオブジェクトを参照することはできます。
Shape shape = new Shape();
shape.draw();
ユーザーがそれらを正しく使用し、無駄を減らすようにガイドする抽象クラスの導入。空のメソッド本体の代わりに抽象メソッドを使用すると、サブクラスはメソッドを実装する必要があり、それを無視することはできません。無視すると、コンパイルエラーが発生します。
抽象クラスとインターフェースは根本的に異なります。インスタンス変数はインターフェースで定義できませんが、抽象クラスは定義できます。クラスは複数のインターフェイスを実装できますが、継承できるのは1つのクラスのみです。
抽象クラスとインターフェースは、置換ではなく協調しています。これらはしばしば一緒に使用され、インターフェース宣言機能、抽象クラスはデフォルトの実装を提供し、メソッドのすべてまたは一部を実装します。多くの場合、インターフェイスには対応する抽象クラスがあります。たとえばCollection
、インターフェイスに対応するAbstractCollection
抽象クラス。
特定の能力を必要とする特定のクラスには、2つの選択肢があります。1つはインターフェイスを実装してすべてのメソッドを自分で実装する方法、もう1つは抽象クラスを継承して必要に応じてメソッドを書き直す方法です。継承の利点は、コードを再利用することです。必要な部分を書き直すだけで済みます。作成する必要のあるコードは比較的小さく、実装が簡単です。ただし、この具象にすでに親クラスがある場合は、インターフェースの実装のみを選択できます。
内部クラスの本質
一般的に、各クラスは独立したJavaソースファイルに対応します。ただし、クラスは、内部クラスと呼ばれる別のクラス内に配置することもできます。比較的言えば、それを含むクラスは外部クラスと呼ばれます。
内部クラスはJavaコンパイラの概念にすぎません。Java仮想マシンの場合、内部クラスは認識されません。各内部クラスは最終的に独立したクラスにコンパイルされ、独立したバイトコードファイルが生成されます。
内部クラスは、外部クラスのプライベート変数に簡単にアクセスできます。
Javaでは、定義の場所と方法に応じて、4つの主要なタイプの内部クラスがあります。
- 静的内部クラス
- メンバー内部クラス
- メソッド内部クラス
- 匿名の内部クラス
メソッド内部クラスはメソッド内で定義および使用され、匿名内部クラスのスコープは小さく、これら2つのタイプはどちらも外部で使用できません。メンバー内部クラスと静的内部クラスは外部で使用できますが、すべて次のように宣言できます。private
静的内部クラスは、静的変数および静的メソッドと同じ方法で、すべてstatic
キーワードを使用して定義されます。
public class Outer{
public static class StaticInner{
public void innerMethod() {
...
}
}
}
静的内部クラスは、外部クラスの静的変数とメソッドに直接アクセスできますが、インスタンス変数にはアクセスできません。
public
静的内部クラスは外部で使用できますが外部类.静态内部类
、ある方法で使用する必要があります。
Outer.StaticInner si = new Outer.StaticInner();
si.innerMethod();
メンバー内部クラスと静的内部クラスの定義は似ていますが、static
修飾子はありません。
public class Outer{
private int a = 100;
public class Inner{
public void innerMethod() {
System.out.prinln("outer a = " + a);
Outer.this.action();
}
}
private void action() {
System.out.println("action");
}
public void test() {
Inner inner = new Inner();
inner.innnerMethod();
}
}
メンバーの内部クラスは、外部クラスのインスタンス変数とメソッドに直接アクセスすることもできます。また外部类.this.xxx
、メソッドを使用して、外部クラスのインスタンス変数とメソッドを参照することもできます。ただし、後者は通常、メンバーの内部クラスのメソッドと変数が外部クラスと同じ名前である場合に使用されます。
列挙の本質
列挙型enum
は特殊なデータ型であり、その値は制限されており、列挙型であるため、列挙型と呼ばれます。
列挙クラスの定義例は次のとおりです。
public enum Size {
SMALL, MEDIUM, LARGE
}
列挙型enum
はキーワードを使用して定義され、列挙型の各値はコンマで区切られます,
。列挙型は、個別のファイルとして定義することも、他のクラス内で定義することもできます。
列挙型変数の定義は次のとおりです。
Size size = Size.MEDIUM;
列挙型変数のtoString()
sumname()
メソッドは、列挙型変数size.toString()
のリテラル値を返し、戻り値はMEDIUMです。
列挙型変数を使用==
してequals()
比較できます。列挙値は順番に並んでいるため、サイズを比較できます。列挙値の順序は、列挙型が定義されたときに0から始まるように決定されます。列挙値はメソッドによってordinal()
返されます。
列挙型はすべてJavaAPIComparable
インターフェースを実装しており、メソッドを介しcompareTo
て他の列挙値と比較できます。これは実際には比較のordinal
サイズです。
列挙型switch
は判定条件で使用できますがcase
、ラベルの前に列挙型を付けることはできません。
switch(size) {
case SMALL:
...
break;
case MEDIUM:
...
break;
case LARGE:
...
break;
}
列挙型にはvalueOf(String)
、文字列に対応する列挙値を返すことができる静的メソッドがあります。
System.out.println(Size.valueOf("SMALL"));
列挙型にvalues
は、宣言と同じ順序で、すべての列挙型変数を含む配列を返す静的メソッドもあります。
for(Size size : Size.values()) {
System.out.println(size);
}
実際、クラスで静的整数変数を定義することも列挙型の機能を実現できますが、列挙型には次の利点があります。
- 列挙を定義するための構文はより簡潔です。
- 列挙型はより安全です。列挙型変数。その値はnullであるか、列挙値の1つであり、他の値にすることはできません。整数変数を使用する場合、その値を制限することはできません。
- 列挙型には、使いやすい組み込みメソッドが多数あります。
列挙型は、実際にはJavaコンパイラによって対応するfinal
クラスにコンパイルされます。このクラスは、JavaAPIのjava.lang.Enumクラスを継承します。
通常、列挙型変数は対応するクラス変数に変換されます。switchステートメントでは、列挙値は対応する順序値に変換されます。列挙型は実際にはクラスですが、コンパイラーは自動的に多くの作業を行うため、列挙型の使用はより簡潔で安全かつ便利です。
列挙型には、インスタンス変数とメソッドを含めることもできます。列挙値の定義;
は、クラス定義の最初と最後に配置する必要があります。
public enum Size {
SMALL("S", "小号"),
MEDIUM("M","中号"),
LARGE("L","大号");
private String attribute;
private String title;
private Size(String attribute, String title) {
this.attribute = attribute;
this.title = title;
}
public String getAttribute() {
return this.attribute;
}
public String getTitle() {
return this.title;
}
}