Javaでは、クラスを継承するのにextends (拡張) キーワードを使用します。
class クラス名 extends 継承するクラス名 { }
クラスによるインターフェイスの継承は、実装として定義します。
クラスは、new演算子によりインスタンス化できます。
new コンストラクタ;
しかしクラスの定義によっては、単純にインスタンス化できないこともあります。
class A {} class B<E> {} // ジェネリック クラス abstract class C {} // 抽象クラス interface D {} // インターフェイス class E { private E(){} } // privateなコンストラクタを持つクラス
new A(); // OK new B(); // ERROR : B is a raw type. References to generic type B<E> should be parameterized new C(); // ERROR : Cannot instantiate the type C new D(); // ERROR : Cannot instantiate the type D new E(); // ERROR : The constructor E() is not visible
分類 | 修飾子 | 説明 |
---|---|---|
アクセス | 未指定 | 所属するパッケージからのアクセスのみを許可する |
public | すべてのアクセスを許可する | |
継承 | abstract | 抽象クラスとして継承を強制する |
final | クラスの継承を禁止する | |
アノテーション | @[アノテーション型] | アノテーションを適用する |
演算モード | strictfp | 浮動小数点演算を厳密に行うFP-strictモードとする |
修飾子は複数指定することができ、その順序は問われません。しかしabstractとfinalは同時に指定できません。それは継承の強制と禁止という、矛盾した指示になるからです。
abstract final class MyClass {}
// ERROR : The class MyClass can be either abstract or final, not both
publicとしたクラスは、必ずそのクラスと同名のファイルで定義しなければなりません。つまりクラスMyClassを定義するファイルは、MyClass.javaというファイル名にします。それを、たとえば
public class A {}
をB.javaというファイルで定義すると、「The public type A must be defined in its own file」のようにエラーとなります。
抽象クラスはインスタンス化できません。サブクラスを定義し、それをインスタンス化します。
abstract class MyClass {} class SubClass extends MyClass {}
MyClass myClass = new MyClass(); // ERROR : Cannot instantiate the type MyClass SubClass subClass = new SubClass(); // OK
final class MyClass {}
class SubClass extends MyClass {} // ERROR : The type SubClass cannot subclass the final class MyClass
アクセスを制限する修飾子です。
修飾子 | クラス内 | サブクラス | 同一パッケージ | 他クラス |
---|---|---|---|---|
未指定 | ○ | × | ○ | × |
private | ○ | × | × | × |
protected | ○ | ○ | ○ | × |
public | ○ | ○ | ○ | ○ |
privateとprotectedは、メンバにのみ使用できます。またC#とは異なり、アクセス修飾子の省略はprivateとはならず、特別な意味を持ちます。
クラスは簡単な初期化を行うために、コンストラクタを持つことができます。
class MyClass { MyClass() {} MyClass(int a) {} }
コンストラクタは、クラスのメンバではありません。
コンストラクタを明示的に定義しないとき、引数なしコンストラクタが自動で定義されます。それをデフォルトコンストラクタと呼びます。
コンストラクタをprivateとすることで、クラスがインスタンス化されるのを防ぐことができます。Preventing Instantiation of a Class - Chapter 8. Classes
class MyClass { private MyClass() {} }
たとえば、このクラスをインスタンス化しようとすると
new MyClass(); // ERROR : The constructor MyClass() is not visible
のようにエラーとなります。
引数付きコンストラクタと引数なしコンストラクタを両方提供するには、両方のコンストラクタを定義する必要があります。
class MyClass1 { } class MyClass2 { MyClass2(int a) {} } class MyClass3 { MyClass3() {} MyClass3(int a) {} }
new MyClass1(); // OK new MyClass2(); // ERROR : The constructor MyClass2() is undefined new MyClass3(); // OK
引数にそのクラスと同じ型を受け取り、渡されたオブジェクトのコピーとなるようにオブジェクトを生成するコンストラクタを、コピーコンストラクタと呼称します。
class MyClass { int a; MyClass(MyClass obj) { this.a = obj.a; } }
class MyClass {
}
class SubClass extends MyClass {
SubClass() {} // OK
}
class MyClass {
MyClass() {}
}
class SubClass extends MyClass {
SubClass() {} // OK
}
スーパークラスが引数付きのコンストラクタを定義しているならば、サブクラスはそのコンストラクタを呼び出し、明示的に初期化を行わなくてはなりません。
class MyClass { MyClass(int a) {} } // ERROR : Implicit super constructor MyClass() is undefined for default constructor. Must define an explicit constructor class SubClass1 extends MyClass { } class SubClass2 extends MyClass { // ERROR : Implicit super constructor MyClass() is undefined. Must explicitly invoke another constructor SubClass2() {} }
class MyClass { MyClass(int a) {} } class SubClass extends MyClass { SubClass() { super(1); // OK } SubClass(int a) { super(a); // OK } }
フィールドの初期化を行う初期化子 (initializer) は、定数以外に他のフィールドやメソッドなどの式を指定できます。
class MyClass { int a = 123; double b = 1.0; int c = a + 1; double d = Math.sqrt(2); }
フィールドを初期化しなかった場合は、その型に応じて次の値に初期化されます。
型 | 初期値 |
---|---|
boolean | false |
char | '\u0000' |
byte | 0 |
short | |
int | |
long | |
float | +0.0 |
double | |
オブジェクト参照 | null |
フィールドを初期化するメソッドは、コンストラクタより先に呼ばれます。
class MyClass { // コンストラクタ MyClass() { System.out.print("Z"); } int A() { System.out.print("A"); return 1; } int a = A(); // 初期化子 } public class TestClass { public static void main(String[] args) { new MyClass(); // AZと出力 } }
初期化を伴わないメソッドの呼び出しはできません。これはメソッドの宣言と誤解されます。また初期化には戻り値が必要なことから、戻り値がvoidのメソッドは呼び出せません。
class MyClass { int A() { return 1; } void B() {} int a= A(); // OK A(); // ERROR : This method requires a body instead of a semicolon int b = B(); // ERROR : Type mismatch: cannot convert from void to int }
staticメソッドをクラス定義のブロックから呼び出そうとすると、Syntax errorとして検出されます。
class MyClass { static int C() { return 1; } int c = MyClass.C(); // OK MyClass.C(); // ERROR : Syntax error on token "C", Identifier expected after this token System.out.print("A"); // ERROR : Syntax error on token "print", Identifier expected after this token }
より複雑な初期化を行うには、初期化ブロックを使用します。これはすべてのコンストラクタの前に実行されます。
class MyClass { int a; { // ここが初期化ブロック a = 123; // } // }
クラスのstaticメンバを初期化します。
class MyClass { static int a; static { a = 123; } }
メソッドに対しても、クラスと同様のアクセス修飾子を指定できます。
メソッドをオーバーライドするとき、スーパークラスにおける可視性 (visibility) を減少させることはできません。
class MyClass { void A() {} private void B() {} protected void C() {} public void D() {} } class SubClass extends MyClass { void A() {super.A();} // OK void B() {super.B();} // ERROR : The method B() from the type MyClass is not visible void C() {super.C();} // ERROR : Cannot reduce the visibility of the inherited method from MyClass void D() {super.D();} // ERROR : Cannot reduce the visibility of the inherited method from MyClass }
抽象メソッドを定義するならば、クラスもabstract (抽象) としなければなりません。
class MyClass1 { // ERROR : The type MyClass must be an abstract class to define abstract methods abstract void A(); } abstract class MyClass2 { // OK abstract void A(); }
抽象メソッドには、本体を定義してはなりません。抽象メソッドでなければ当然、本体が必要です。
abstract class MyClass { abstract void A(); // OK abstract void B() {} // ERROR : Abstract methods do not specify a body void C() {} // OK void D(); // ERROR : This method requires a body instead of a semicolon }
すべてのメソッドを抽象とするならば、クラスではなくインターフェイスとして定義します。
finalとされたメソッドは、サブクラスでオーバーライドできません。
class MyClass { void A() {} final void B() {} } class SubClass extends MyClass { void A() {} // OK void B() {} // ERROR : Cannot override the final method from MyClass }
メソッドのパラメータリストの最後のパラメータは、特定の型の可変長の引数として宣言できます。そのためには、パラメータ型の後にエリプシス (ellipsis) を記述します。次の例ではint...の「...」が、それにあたります。
void Method(int... a) {}
可変長引数は、必ずパラメータの最後でなければなりません。
void Method1(int a, int... b) {} // OK void Method2(int... b, int a) {} // ERROR : The variable argument type int of the method Method2 must be the last parameter
可変長引数は、メソッド内では配列となります。たとえばメソッドが、
void Method(int... a) {}
と定義されているとします。このとき次のように引数を与えて呼び出すと、メソッド側にはコメントで示すように渡されます。
Method(); // a.lengthは0 Method(1); // a.lengthは1、a[0]は1 Method(1,2); // a.lengthは2、a[0]は1、a[1]は2
メソッドにアノテーションの@Overrideを指定することで、オーバーライドしていることを明示できます。
内部クラスとは、staticではないネストしたクラス (nested classes) のことです。
ネストしたインターフェイスは暗黙的にstaticとなるため、たとえstatic修飾子を省略しても非staticとはなりません。
またネストした型を囲む (enclose) 型は、エンクロージング型 (enclosing type) と呼ばれます。そしてそれがクラスの場合にはエンクロージング クラスとなり、そのクラスのオブジェクトはエンクロージング オブジェクトと呼称されます。
内部クラスの名前は、「エンクロージング クラス名.内部クラス名」の形式で表現されます。
class MyClass { private int x = 1; // ここはクラスのコードブロック内 class InnerClass { InnerClass() { // エンクロージング オブジェクトにアクセスできる x++; // より明示的に記述すると次のようになる MyClass.this.x++; } } void Method() { InnerClass innerClass1 = new InnerClass(); // より明示的に記述すると次のようになる InnerClass innerClass2 = this.new InnerClass(); } }
ローカル内部クラスとは、メソッドなどのコードブロック内で定義される内部クラスです。これはクラスのメンバではありません。
class MyClass { void Method() { // ここはメソッドのコードブロック内 class InnerClass { } // 定義されているコードブロック内でのみインスタンス化できる InnerClass innerClass = new InnerClass(); } }
無名内部クラスとは、newでインスタンス化されるときに定義されるローカル内部クラスです。
クラスやインターフェイスを継承する必要があり、暗黙的にimplements修飾子を持っています。
class MyClass { void Method() { // ここはメソッドのコードブロック内 Object innerClass = new Object() { // ここは内部クラスのブロック内 // 内部クラスのメソッドの定義 @Override public String toString() { return "abc"; } }; } }
オブジェクトは参照されなくなると、ガベージコレクタによって自動的に削除されます。ただし削除されるタイミングは、参照されなくなった時点とは限りません。