S-JIS[2008-06-15/2016-12-29] 変更履歴

Javaのクラスの使用

Javaのクラスやインターフェース等の使い方(インスタンス化の方法等)。


クラス

クラスをソース上で使用する為には、まず、そのクラスにアクセス可能である必要がある。
(アクセス可能(参照可能)でないと、importでコンパイルエラーになる)

クラス(インスタンス)を使うには、以下のように変数を宣言する。(Fooというクラスの例)[2003-07-06]

	void メソッド() {
		Foo a;
		〜
	}

ローカル変数を宣言すると、初期値は設定されない。そのまま値を代入せずに使おうとするとコンパイルエラーになる。
フィールド(メンバー変数)を定義すると、デフォルトの値はnullになる)
(いずれにしても、C++と異なり、変数宣言時点では実体(インスタンス)を持つわけではない)

実体(インスタンス)は、具象クラスに対してnewで生成する。 (抽象クラスインターフェースに対してはnewではインスタンス生成できない)

	Foo a;
	a = new Foo();
	Foo a = new Foo();

コンストラクターに引数がある場合は、newする際に引数を指定する。

	Foo a = new Foo("zzz", 123);


newでインスタンス化するには、コンストラクターアクセス可能(例えばpublic)である必要がある。
アクセス可能でない(例えばprivate)場合は、newではインスタンス化できない。
その場合は、インスタンス化の方法が他の手段で提供されているはず。(getInstance()とか)

インスタンス化される際のフィールドの初期化順序
newの後ろにクラス定義を付ける(無名内部クラス)

コンストラクター内のコーディング


対象となるクラスがパッケージに属している場合は、パッケージを明示することが出来る。[2008-06-15]

	jp.hishidama.sample.Foo foo = new jp.hishidama.sample.Foo();

しかし一々書くのは面倒なので、importしてしまうのが楽。

import jp.hishidama.sample.Foo;

	Foo foo = new Foo();

(java.util.Dateとjava.sql.Dateのようにクラス名がかぶっている場合は、どちらか一方しかimportできないので、他方は必ずパッケージを指定する必要がある)

クラスだけでなく、staticなフィールドやメソッドをインポートすることも出来る。(JDK1.5以降)

import static java.util.Math.PI; //MathクラスのPIというフィールド

自分自身のインスタンスを表すにはthisを使う。[2007-06-30]

class Foo {
	void method() {
		Foo f = this;
	}
}

自分自身のクラス名を付けてthisを使うことも出来る。[2016-12-17]
(この使い方は、たいていの場合は内部クラスから外側のクラスを参照するのに使う)

class Foo {
	void method() {
		Foo f = Foo.this;
	}
}

thisを使ったメソッド呼び出し
thisを使ったフィールドアクセス
内部クラスでのクラス名.this
コンストラクターでのthis()


継承元の親クラス(スーパークラス)を表すにはsuperを使う。[2007-06-30]

class Zzz extends Foo {
	@Override
	protected void method() {
		super.method();
	}
}

自分自身のクラス名を付けてsuperを使うことも出来る。[2016-12-17]

class Zzz extends Foo {
	@Override
	protected void method() {
		Zzz.super.method();

		// 単独でsuperを使うことは出来ない
//×		System.out.println(super);
//×		System.out.println(Zzz.super);
	}
}

superを使ったメソッド呼び出し
superを使ったフィールドアクセス
「インターフェース名.super」
コンストラクターでのsuper()
内部クラスを生成する為のsuper()


コンストラクターのコーディング

コンストラクター内においては、親クラスのコンストラクター呼び出しにsuper()が使える。[2008-08-24]
また、自クラスの別コンストラクターを呼び出すことも出来る。その場合はthis()という形をとる。
super()もthis()も、引数を指定可能(引数付きコンストラクターを呼び出す)。

super()とthis()は、一つのコンストラクター内では、どちらか片方のみ指定可能。どちらも省略した場合は、暗黙に「super();」が指定されたものとして扱われる。

class Foo {
	private String str;

	Foo() {
		this(null);	//別コンストラクターを呼び出す
				//複数のコンストラクターが在る場合、引数の種類が合致するものが呼ばれる
	}

	Foo(String str) {
		this.str = str;
	}
}
class Zzz extends Foo {

	Zzz() {
		super();
		//もし親クラスに該当コンストラクターが存在していなければ、コンパイルエラーになる
	}

	Zzz(int a) {
		//何も指定しない場合は暗黙にsuper();が呼び出される
	}

	Zzz(String s) {
		super(s);	//親クラスの引数がStringのコンストラクターを呼び出す
	}
}


super()やthis()は必ずコンストラクター内の先頭になければならない為、ローカル変数と言えども使用できない。

	コンストラクター(String s) {
×		int n = (s == null) ? 0 : s.length();
		super(n);
	}

	コンストラクター(String s) {
○		super((s == null) ? 0 : s.length());
	}

親クラスの方が先に初期化されてないとまずいとは思うので、super()の呼び出しより前にフィールドを使うことが出来ないのは納得だけれども、ローカル変数くらい使えてもいいような気はするんだけどなー。

“総称型のコンストラクター”の定義・呼び出し方法


メソッド呼び出し

メソッドを呼び出すには、メソッド名の後ろに丸括弧()を付ける。メソッドによってはここに引数を指定する。[2008-08-24]

	myMethod1();	//自クラス(継承されていれば、そのサブクラス)のメソッドを呼び出す
	myMethod2(1, 2, 3);

可変長引数のメソッドの呼び出し


他クラスのstaticメソッドを呼び出す場合は、接頭辞としてクラス名を前に付ける。

	Foo f = Foo.getInstance();	//Fooクラスのstaticメソッドを呼び出す
	Foo.func(1, 2, 3);

インスタンスメソッドを呼び出す場合は、接頭辞としてインスタンス(の変数)を前に付ける。

	Foo f = 〜;
	f.method();	//Fooのインスタンスメソッドを呼び出す
	f.set("abc");

このとき、変数の値がnullだとNullPointerExceptionが発生する。

インスタンスメソッドの呼び出し(super以外)では、ソース上に指定したクラス(メソッド呼び出しの接頭辞)のメソッドではなく、実行時のインスタンスのメソッド(サブクラスにオーバーライドされたメソッドがあれば、そのメソッド)が呼ばれる。
インスタンスメソッドはvirtual扱い

staticメソッドに対してもインスタンスメソッドと同じくインスタンスを接頭辞にした指定をすることは出来るが、インスタンスの内容に関わらず、その変数のクラスに対するメソッド呼び出しとなる。[2009-03-28]
(変数内のインスタンスのstaticメソッドが呼ばれるわけではない。変数の値がnullであっても問題なくメソッド呼び出しは行われる。
 ただし、Eclipseでコンパイルすると「staticメソッドには static にアクセスする必要があります」という警告が出る)


インスタンスメソッドで何も接頭辞を付けていない場合は、thisが付けられているのと同じ扱い。

	this.myMethod1();
	this.myMethod2(1, 2, 3);

しかし、自クラスのインスタンスメソッド呼び出しなら、普通はthisは省略する。

staticメソッドで何も接頭辞を付けていない場合は、自クラス名が付けられているのと同じ扱い。[2016-12-29]
しかし、自クラスのstaticメソッドの呼び出しなら、普通はクラス名は省略する。


継承元の親クラスのインスタンスメソッドを呼び出す場合はsuperを付ける。[2007-06-30]

	super.toString();
	super.parentMethod(1, 2);

ただし、親クラスのメソッドが抽象メソッド(そのメソッドが実装されていない)の場合、superを使った呼び出しは出来ない(コンパイルエラー)。[2008-12-22]


“総称型のメソッド”を呼び出す方法
synchronizedされたメソッドの排他のされ方


フィールドアクセス

クラスフィールドにアクセスする(読み書きする)には、クラス名+ピリオド+フィールド名(変数名)を指定する。
インスタンスフィールドにアクセスするには、そのインスタンス変数名+ピリオド+フィールド名を指定する。[2009-03-28]

		int staticValue   = クラス名.クラスフィールド名;
		int instanceValue = 変数名.インスタンスフィールド名;

インスタンスフィールドの場合、変数の値がnullだったらNullPointerExceptionが発生する。
クラスフィールドの場合、インスタンス変数と同様の指定の仕方も出来るが、その変数に指定されたクラスに対してのアクセスとなる。(ただし、推奨される方法ではない)

class F1 {
	static String svalue = "F1";
}

class F2 extends F1 {
	static String svalue = "F2";
}

		F1 var = new F2();
		System.out.println(var.svalue);	→「F1」が表示される

上記の例は、変数varがF1として定義されているので、「var.value」は「F1.value」と同じ意味になる。
中身が何のインスタンスであっても関係ない。変数の中がF2のインスタンスであってもF2.svalueにはアクセスされないし、変数の値がnullであっても問題なく実行される。

そういう訳で、こういうアクセスの仕方はプログラマーの誤解を誘発する可能性があるので、Eclipseでコンパイルすると 「staticフィールドには static にアクセスする必要があります」という警告が発せられる。


自分のクラス内のフィールド(クラスのメンバー変数)を指定するには、フィールド名(変数名)をそのまま書けばよい。[2008-08-24]
( 「クラス名.」や 「変数名.」が省略できる)

class クラス {
	int value = 1;
	static int svalue = 2;

	void メソッド() {
		System.out.println(value);
		System.out.println(svalue);
	}
}

ただ、明示的にフィールドであることを示す為に、thisが使える(インスタンスフィールドの場合)。
特にフィールドとローカル変数は同じ変数名を使えるので、その際にフィールドにアクセスするには必須。[2008-06-15]

class クラス {
	int value = 123;	//フィールド

	void メソッド() {
		int value = 456;	//ローカル変数

		System.out.println(value);
		System.out.println(this.value);
	}
}

スーパークラス(親クラス)で定義されているフィールドと全く同名のフィールドを定義することが出来る。[2008-08-31]
この場合、単純名では(=super.等を付けなければ)親クラスのフィールドにアクセスできなくなる。これを隠蔽(hide)と言う。

親クラスフィールドには、キャストすることでアクセスできる。[2008-08-09]

interface T0 {
	int x = 0;	//実際は、public static final int
}
class T1 implements T0 {
	int x = 1;
}
class T2 extends T1 {
	int x = 2;
}
class T3 extends T2 {
	int x = 3;

	public void print() {
		System.out.println("this: " + this.x);
		System.out.println("super:" + super.x);

		System.out.println("T3: " + ((T3)this).x);
		System.out.println("T2: " + ((T2)this).x);
		System.out.println("T1: " + ((T1)this).x);
		System.out.println("T0: " + ((T0)this).x); //Eclipseでは「staticアクセスなのでT0.xにしろ」という警告が出る
	}
}

「new T3().print()」を実行すると、以下のようにちゃんと各クラスの値が取れている。

this: 3
super:2
T3: 3
T2: 2
T1: 1
T0: 0

キャストというか、クラスを指定した変数に代入しても同じ効果が得られる。

		T3 t3 = new T3();
		T2 t2 = t3;
		T1 t1 = t3;
		T0 t0 = t3;
		System.out.println(t3.x);
		System.out.println(t2.x);
		System.out.println(t1.x);
		System.out.println(t0.x);	 //Eclipseでは「staticアクセスなのでT0.xにしろ」という警告が出る

ただし、この方法はあくまでもフィールド(メンバー変数)に対してしか使えない
メソッドの場合はvirtual扱いなので(=オーバーライドされるので)、どのクラスの変数に代入しようが、実際のインスタンスの末端のサブクラスのメソッドが呼ばれる。
(フィールドはオーバーライドされないので、指定されるクラスによって参照先が異なる)


内部クラスから外側クラスへのアクセス

内部クラスから、外側のクラスへアクセス(変数の読み書きやメソッドの呼び出し)することが出来る。[2007-02-07]
内部クラスは外部クラスの一部分と考えられているようで、privateなメンバーにもアクセスできる。
ただしstaticな内部クラスの場合は、外部クラスのインスタンスメンバー(staticでないメンバー)にはアクセスできない。

class 外側クラス {
	private int outerField = 0;

	private void outerMethod() {
	}

	class 内部クラス {
		public void innerMethod() {
			System.out.println(outerField);
			outerMethod();
		}
	}
}

内部クラスと外側クラスに同名(かつ引数の型が同じ)のメソッドがある場合は、内部クラスからそのメソッドを呼び出すと、当然、内部クラス側のメソッドが呼ばれる。[2016-12-17]
しかし同名メソッドだが引数の型が異なる場合、内部クラス側のメソッドを呼ぼうとして、引数が異なるのでコンパイルエラーとなる。

class Outer {

	private void method(String s) {
		System.out.println(s);
	}

	class Inner {
		private void method(int n) {
			System.out.println(n);
		}

		public void run() {
			method(123);
//×			method("abc"); //「The method method(int) in the type Outer.Inner is not applicable for the arguments (String)」というコンパイルエラー
		}
	}
}

この場合は、クラス名を明示的に付けてOuter.this.method("abc")」とすればよい。

参考: bitter_foxさんのnesting printhing


内部クラスから外側クラスを指すには「クラス名.this」を使う。[2007-02-07]

内部クラスにおいては、thisは当然自分自身のインスタンスを指すが、「外側クラス名.this」とすることで、外側クラスのインスタンスを指定できる。
内部クラスの中で同じ内部クラスを再帰的にインスタンス化しても、いずれの内部インスタンスからでも外側クラスにアクセスすることが出来る。
(要するに、内部クラスは外側クラスのインスタンスを暗黙に保持しているようだ。だからnewするときに外側クラスのインスタンスが必要になるわけ。
そしてstaticな内部クラスであれば、外側クラス内では共通だから、外側クラスのインスタンスは不要。逆に外側クラスのインスタンスメンバーにアクセスできなくなる)

public class Root {

	public static void main(String[] args) {
		Root r = new Root();
		r.print();
	}

	private int n = 0;

	private void print() {
		System.out.println("Root: " + this);     	//自分自身
		System.out.println("Root: " + Root.this);	//自分自身の別表記
		Inner i = new Inner();
		i.print();
		System.out.println(n);
	}

	class Inner { // 内部クラス
		private void print() {
			System.out.println("Inner: " + this);
			System.out.println("Inner: " + Root.this);
			n++;

			new InIf() { // 無名内部クラス
				public void print() {
					System.out.println("InIf: " + this);
					System.out.println("InIf: " + Inner.this);
					System.out.println("InIf: " + Root.this);
					n += 10;
				}
			}.print();
		}
	}
}

interface InIf {
	void print();
}

局所クラス無名内部クラス)の場合、finalなローカル変数にもアクセスできる


同様に、「クラス名.super」という書き方も出来る。[2008-09-06]

class Super {
	protected int x = 1;
}

class Outer extends Super {

	private int x = 2;

	class Inner {
		void method() {
			System.out.println(Outer.super.x);
			System.out.println(Outer.this.x);
			System.out.println(x);
		}
	}
}

他所の内部クラスを継承したクラス(以下の例のSub1)を作る場合、コンストラクターがちょっとややこしい。[2008-09-14]

class Outer1 {
	Outer1() { //Outer1のコンストラクター
		System.out.println("outer1");
	}

	class Inner1 { //Inner1のコンストラクター
		Inner1() {
			System.out.println("inner1");
		}
	}
}

class Sub1 extends Outer1.Inner1 {	//Inner1から派生
	Sub1() {
		new Outer1().super();
		System.out.println("sub1-0");
	}

	Sub1(Outer1 x) {
		x.super();
		System.out.println("sub1-1");
	}
}
		Sub1 sa = new Sub1();
		Sub1 sb = new Sub1(new Outer1());

このSub1Inner1を継承しているので、Sub1のコンストラクターから呼び出しているsuper()は、Inner1のコンストラクター呼び出しとなる。

しかしInner1は(Outer1の)内部クラスなので、Inner1のインスタンス生成(初期化)にはOuter1のインスタンスが必要となる。
そこで、super()の前に(Inner1が必要とする)Outer1インスタンスを指定してやる必要がある。

(ただ単に外側から内部クラスのインスタンスを生成する方法は、「Outer1 x」に対して「x.new Inner1()」となる。
x.new Inner1()」ならxの中のInner1を作ってるんだなーという気になるけど、「x.super()」だとxの親クラスのコンストラクター呼び出しに見えてしまうような?(苦笑))


インターフェース

AというクラスがIというインターフェースを継承(実装)している(class A implements I)場合、以下のような使い方が出来る。[2003-07-06]

interface I {
	public void hoge();
}

class A implements I {

	@Override
	public void hoge() {
		〜
	}
}
	I obj = new A();
	obj.hoge();	//hoge()はIで定義され、Aで実装されている。ので、実際にはAのhoge()が呼ばれる

この場合、Aで定義されているメソッドが色々あっても objはIとして宣言されているので、ソース上では Iで定義されているメソッドしか呼べない。
Aに有ってIに無いメソッドをobjで呼ぼうとするとコンパイルエラーになる。


自分でnewするときにはあまり有り難味が無いが、具体的な実装を気にしない場合には便利。

	List list = new ArrayList(10);	//Listはインターフェース、ArrayListは具象クラス
//	List list = new LinkedList();	//LinkedListは具象クラス
	list.add("abc");			//インターフェースで変数を定義しておけば、使う側はそのメソッドで扱える。

インターフェースを使っておくと、コーディング上は具象クラスを気にせずに出来るというメリットがある。
ただし、具象クラスによって処理に向き不向きがあったりするので、内容を全く気にしないでよいかというと、そうでもないこともある。
(→ArrayListとLinkedList

メソッドの引数にインターフェースを使うのが便利。

	public byte[] read(InputStream is) {	//InputStreamはインターフェース
		〜
	}

	public String getData(Map map) {	//Mapはインターフェース
		return (String) map.get("data"); 
	}
	//呼び出し例
		read(new FileInputStream(〜));
		read(new ByteArrayInputStream(〜));

		getData(new HashMap());
		getData(new TreeMap());

インターフェースを定義する方法


アダプタクラス

アダプタクラスは、文法ではなくて使用法の名称。[2003-07-06]

インターフェースはメソッドが全てabstract(抽象メソッド)なので、「一部だけ自前で書きたい」という場合でも全てのメンバーをオーバーライドしなければならず、不便である。
このような要望に応えるため、アダプタと呼ばれるクラスが用意されていることがある。
アダプタクラスはインターフェースを実装し、デフォルト動作のメソッド(大抵は何もしない、空のメソッド)が定義される。
プログラマーはアダプタクラスを継承したクラスを作り、必要なメソッドだけ記述(オーバーライド)して使用する。

→アダプタクラスの例:ファイルマウス


無名内部クラス(匿名クラス)

一箇所でしか使用しない場合にわざわざクラスを作るのは面倒なので、無名内部クラス(匿名クラス)という文法が用意されている。[2003-07-06]
(→(無名でない)内部クラス
(無名内部クラスは、局所クラスの特殊形態らしい。[2008-06-15]

無名内部クラスは、newでインスタンスを生成する際に、その後ろにクラスを定義するような文を続ける。

	new クラスやインターフェース() {
		〜	//必要なメソッドだけオーバーライド、あるいは新しいメソッドを追加
	}

これは、以下のようなクラス(やインターフェース)をnewしたものと同じ意味となる。

class 匿名希望 extends クラス {
	〜	//必要なメソッドだけオーバーライド、あるいは新しいメソッドを追加
}

class 匿名希望 implements インターフェース {
	〜	//インターフェースの全メソッドを実装(および、新しいメソッドを追加)
}

無名内部クラスの文法は、(アダプタクラスだけじゃなくて)普通のクラスインターフェースでも使える。[2007-05-02]
インターフェースで使った場合、全メソッドを実装しないといけなくなるから大変かもしれないけど(苦笑)

ただし、複数のインターフェースを実装するような書き方は出来ない。[2010-01-09]
そういうクラスは無名でなくちゃんと定義すべきだろう。しかしSerializableを別途実装するような書き方はしたい事があるかもしれないなぁ…。

例:

	// 作ったオブジェクトを渡す例
	System.out.println(new Object() {
		@Override
		public String toString() {
			return "無名内部クラス";
		}
	});

	// メソッドを作り、そのメソッドを呼ぶ例
	new Object() {
		void test() {
			System.out.println(this.getClass());
		}
	}.test();

	// 一時的な別スレッドを実行する例
	new Thread() {
		@Override
		public void run() {
			System.out.println(this.getClass());
		}
	}.start();   // Thread#start()が呼ばれる。その中で上記のrun()が実行される。

引数つきコンストラクターを使って無名内部クラスのインスタンスを作ることも出来る。[2010-01-09]

	long time = System.currentTimeMillis();
	Date date = new Date(time) {
		@Override
		public String toString() {
			return String.format("%1$tF %1$tT.%1$tL", this);	//YYYY-mm-dd HH:MM:SS.sss
		}
	};

無名内部クラスでは、そのクラスのコンストラクターを定義することは出来ない。[2010-01-09]
コンストラクターはクラスと同じ名前だから、“無名”では定義のしようが無いわなぁ。

しかしインスタンス初期化子を使えば、似たような処理を行うことが出来る。

	List<String> list = new ArrayList<String>() {
		{
			super.add("a");
			super.add("b");
			super.add("c");
		}
	};

→ちなみに、突き詰めるとすごいコーディングになる。初めて見た時はぶっとんだ。かっちょえー(爆)


無名内部クラスから、外側のクラスのメンバー(フィールドやメソッド)にアクセスすることが出来る。[2009-04-15]
クラス名.thisを使って、 外側のクラス名を明示的に指定してアクセスすることも出来る。[2016-12-17]

class 外側クラス {

	private int value;

	void method() {
		new Object() {		// 無名内部クラス
			void test() {
				value++;
				System.out.println(value);

				System.out.println(外側クラス.this.value);
			}
		}.test();
	}
}

内部クラスを使った無名内部クラスを作ると、無名内部クラスからアクセスできる範囲を誤解しそうだ。[2016-12-17]

// Innerの外側のクラス
class Outer {
	public int o = 0;

	// 内部クラス
	class Inner {
		public void method1() {
			System.out.println(this);
			System.out.println(Outer.this);

			System.out.println(o);
			System.out.println(Outer.this.o);
		}
	}
}

// runの属しているクラス
class Another {
	public int a = 1;

	public void run(Outer outer) {
		outer.new Inner() { // 無名内部クラス
			public void method2() {
				System.out.println(this);
//×				System.out.println(Outer.this);
				System.out.println(Another.this);

				System.out.println(a);
				System.out.println(Another.this.a);
			}
		}.method2();
	}
}

Inner内のmethod1からは、Innerの外側のクラスであるOuterにアクセスできる。
new Inner()内のmethod2からは、Innerの外側クラス(Innerを定義している場所の外側)であるOuterは見えない(アクセスできない)。
(new Inner()のレシーバーはOuterなので(あるいはnew Inner()はInnerを継承しているので)(あるいはInnerの暗黙のコンストラクターでOuterを受け取っているので)、method2からOuterにアクセスできそうな気がするが、出来ない)
new Inner()はAnotherのrunメソッド内にあるので、runメソッドから見える範囲(Anotherクラスやrunメソッド内のローカル変数)にはアクセスできる。

参考: bitter_foxさんのnesting printhing


無名内部クラスから、その無名内部クラスを含んでいるメソッド内のローカル変数には限定的にアクセスできる。
すなわち、そのローカル変数がfinal(あるいは実質的final)の場合だけアクセスできる。(つまり読み込み専用)

	void method2() {
		final int value = 111;

		new Object() {		//無名内部クラス
			void test() {
				System.out.println(value);
			}
		}.test();
	}
	void method3(final int value) {

		new Object() {		//無名内部クラス
			void test() {
				System.out.println(value);
			}
		}.test();
	}

finalな変数は、あくまでその変数自身に代入できないというだけなので、それがオブジェクトなら、その中は当然書き換え可能。

	int method4() {
		final int[] ret = new int[1];

		new Object() {		//無名内部クラス
			void test() {
				ret[0] = 456;
			}
		}.test();

		return ret[0];
	}

参考: Sunの特権ブロックAPI


実質的final

JDK1.8では、“実質的にfinal事実上のfinal(effectively final))”な変数にはfinalを付けなくてもよくなった。[2014-03-22]
内部クラスやラムダ式において、その外側で定義されているローカル変数を使う場合は、そのローカル変数に暗黙にfinalが付いているものと見なされる。

	void method2() {
		/*final*/ int value = 111;

		new Object() {		//内部クラス
			void test() {
				System.out.println(value);
			}
		}.test();
	}
	void method3(/*final*/ int value) {

		new Object() {		//内部クラス
			void test() {
				System.out.println(value);
			}
		}.test();
	}
	int method4() {
		/*final*/ int[] ret = new int[1];

		new Object() {		//内部クラス
			void test() {
				ret[0] = 456;
			}
		}.test();

		return ret[0];
	}
	void method5() throws Exception {
		/*final*/ int value = 111;

		Runnable runner = () -> {	//ラムダ式
			System.out.println(value);
		};

		runner.run();
	}

ソースの表面上はfinalが出てこなくなるのだが、実質的にはfinalが付いているので、その変数に再代入を行うとコンパイルエラーになる。
「内部クラスから参照されるローカル変数は、finalまたは事実上のfinalである必要があります」
「Local variable value defined in an enclosing scope must be final or effectively final」

	void method2() {
		int value = 111;

		new Object() {		//内部クラス
			void test() {
				System.out.println(value); //コンパイルエラー
			}
		}.test();

		value = 222; //再代入している
	}
	void method5() throws Exception {
		/*final*/ int value = 111;

		Runnable runner = () -> {	//ラムダ式
			System.out.println(value); //コンパイルエラー
		};

		value = 222; //再代入している

		runner.run();
	}

アノテーション

アノテーションは、JDK1.5からの新機能。[2006-07-26]
ソースを解析する人(コンパイラーとか開発ツール)に対して注釈を伝えるもの。

「このメソッドにはこういう意味があるんだ!」という事をコメントに書いておいても人間(プログラマー)にしか通用しないが、アノテーションとして書くことでコンパイラー(やツール)に伝えることが出来る。

アノテーションは自分で作ることも出来るが、ひとまず定義されているアノテーションは以下のようなもの。

定義されているアノテーション
アノテーション 説明 備考 更新日
@Override スーパークラスのメソッドをオーバーライドしている事を示す。 @Overrideを付けていると、スーパークラスに存在しないメソッドであればコンパイルエラーになる。
オーバーライドしているつもりで間違ったメソッド名を書いてしまったときにエラーとなるので、分かりやすくて便利。
Eclipse3だとソースの左側にオーバーライド印(白や緑の三角形)が付くから、目ざとい人は@Overrideを付けなくても気付くかもしれないが…
 
JDK1.5では、インターフェースを実装したメソッドに対して@Overrideを付けるとコンパイルエラーになる(そのメソッドは直接の親クラスに存在しないから)。
JDK1.6では、エラーにならない。(インターフェースは、論理的には“特殊なクラス”に過ぎないからなぁ)
2008-10-25
@Deprecated 非推奨メソッド(クラス)である事を示す。 非推奨は、JDK1.4まではJavadocに記述していたが、アノテーションでも示せるようになった。  
@SuppressWarnings 指定した種類の“コンパイラーの警告”を抑制する。 @SuppressWarningsに指定する文字列
SerializableserialVersionUIDという定数が無い場合の警告を抑制する例
2007-06-11

これらはいずれもjava.langパッケージ配下のクラス(アノテーション)なので、明示的なimportをせずに使うことが出来る。[2008-07-07]

使用例:

public class Sample {

	@Override
	public boolean equals(Object obj) {
		return super.equals(obj);
	}

	/**
	 * @param args
	 */
	@Deprecated
	public static void main(String[] args) {
	}
}

JUnit4でのアノテーションの使用例

アノテーションが指定できる場所(メソッド定義箇所やらクラス定義箇所やら)はアノテーション毎に決まっている。[2007-11-10]

基本的には、1つの場所に同じアノテーションを複数指定することは出来ない。
が、JDK1.8からは@Repeatableが付けられたアノテーションは複数指定できるようになった。[2014-03-20]


アノテーションを作成する方法 [2007-11-10]


クラス定義方法へ戻る / Java目次へ戻る / 新機能へ戻る / 技術メモへ戻る
メールの送信先:ひしだま