新トップページへ | Tip
アノテーションとかジェネリックの話
LastUpdate : 11/08/06
仕事の都合上、JDK1.4の世界にずーっといたが、新案件でJDK1.6の世界に投げ出された。私の知識はJDK1.5で止まってる(昔自発的にSJC-PだとかいうのをJDK1.5の時代に取得した。よくやる気起きたなwwwwwwwww)。
ちと、いい機会なので、頭の中の情報を整理しつつ、時流にのってみようかと思う。
乗るしかない、このビックウェーブに!(最近JDK1.7もリリースされたしね!この時点で乗り遅れてんじゃん、私・・・orz
サンプルソースはプログラムとして意味のないものがほとんどです(うまい具合にソースがかけんw)。使い方・雰囲気をだすために、書いてます。。。
☆もくじ
- ジェネリクスについて 基本
- ジェネリクスについて メソッドのジェネリクス
- ジェネリクスを指定する際の?(ワイルドカードについて)
- 拡張for文
- 標準アノテーションについて
- 独自アノテーションについて
ジェネリクスは・・・、C++でいう関数テンプレートみたいなもんだと勝手に思ってます。
基本的な使用方法は以下のとおりです。
また、ジェネリックとして指定しているインスタンスは、ソース上、Objectクラスの下位クラスとなるらしく、toStringメソッドやgetNameメソッドが使用可能です。
GenericSample0.java |
public class GenericSample0<T> {
private T val;
public GenericSample0(T val) {
this.val = val;
}
public void showObject() {
//Tにどのようなクラスが指定されるかわからないが、以下のようにObject型として扱うことができる
System.out.println("val : "+val.toString());
System.out.println("class name : "+val.getClass().getName());
}
}
|
呼び出し部分 |
//使い方その1(以下のように型を指定します)
GenericSample0<String> cls1 = new GenericSample0<String>("ゆがみねぇな");
cls1.showObject();
System.out.println();
//使い方その2(インターフェイスも指定可能です)
List<String> list = new LinkedList<String>();
list.add("仕方ないね"); list.add("だらしねぇな"); list.add("ゆがみねぇな");
GenericSample0<List<String>> cls2 = new GenericSample0<List<String>>(list);
cls2.showObject();
System.out.println();
//使い方その3(無論クラスも指定可能です)
Date date = new Date();
GenericSample0<Date> cls3 = new GenericSample0<Date>(date);
cls3.showObject();
System.out.println();
|
実行結果 |
val : ゆがみねぇな
class name : java.lang.String
val : [仕方ないね, だらしねぇな, ゆがみねぇな]
class name : java.util.LinkedList
val : Sat Aug 06 21:21:50 JST 2011
class name : java.util.Date
|
クラスのほか、メソッドに対してもジェネリクスを使えます。
GenericSample0_1.java |
import java.util.List;
public class GenericSample0_1 {
/**
* 引数の型をジェネリクスにしたサンプル
*/
public <T> void showMsg1(T obj) {
System.out.println("val : "+obj.toString());
System.out.println("class name : "+obj.getClass());
}
/**
* ジェネリックで指定した型がどのクラスの下位クラスか・・・なども指定可能です。
* (これはクラスでも、メソッドでも両方可能です)
*/
public <T1, T2 extends List<T1>> String showMsg2(T2 obj) {
StringBuilder strBuilder = new StringBuilder();
for(T1 str : obj) {
strBuilder.append(str+" ");
}
return strBuilder.toString();
}
}
|
呼び出し部分 |
//異なるクラスで、同じメソッドを呼んでみる
GenericSample0_1 smpl = new GenericSample0_1();
List<String> list = new LinkedList<String>();
list.add("仕方ないね"); list.add("だらしねぇな"); list.add("ゆがみねぇな");
smpl.showMsg1(list);
smpl.showMsg1(12);
System.out.println();
//型を指定する際、どのクラスを継承しているか・・・という指定も可能
System.out.println("showMsg2 result : "+smpl.showMsg2(list));
List<Integer> list2 = new ArrayList<Integer>();
list2.add(1); list2.add(10); list2.add(100);
System.out.println("showMsg2 result : "+smpl.showMsg2(list2));
|
実行部分 |
val : [仕方ないね, だらしねぇな, ゆがみねぇな]
class name : class java.util.LinkedList
val : 12
class name : class java.lang.Integer
showMsg2 result : 仕方ないね だらしねぇな ゆがみねぇな
showMsg2 result : 1 10 100
|
型が不明の場合?を指定することができます。
またその?も、どのクラスを継承しているもののみ可能という制限(上限境界をもつワイルドカード)や、〜のクラスまでの下位クラスを設定可能というような制限(下限境界をもつワイルドカード)を設けることが可能です。
Type1.java |
public class Type1 {
private String msg;
public Type1(String msg) {
this.msg = msg;
}
public String getMsg(){ return msg; }
}
|
Type2.java |
public class Type2 extends Type1 {
private String msg2;
public Type2(String msg1, String msg2) {
super(msg1);
this.msg2 = msg2;
}
public String getMsg2() { return msg2; }
}
|
Type3.java |
public class Type3 extends Type2 {
private String msg3;
public Type3(String msg1, String msg2, String msg3) {
super(msg1, msg2);
this.msg3 = msg3;
}
public String getMsg3() { return msg3; }
}
|
GenericSample1.java |
public class GenericSample1 {
/**
* ?を記述する場合のサンプル
* 何の型になるか実行してみてからじゃないとわかんない時は?を記述する
* @param val 文字列
* @return valにnullを指定するとIntegerのList、valに文字列を指定するとStringのListを返す。
*/
public List<?> sampleA(String val) {
List<?> list = null;
if( val == null ) {
List<Integer> tmp = new LinkedList<Integer>();
tmp.add(10000000);
list = tmp;
} else {
List<String> tmp = new LinkedList<String>();
tmp.add(val);
list = tmp;
}
return list;
}
/**
* ?をした際、なんでもよいことになってしまうが、それについて制限を付ける方法 その1
* ?の型は、特定の型の子クラスであること・・・という制限を付ける方法
*/
public List<? extends Type1> sampleB(boolean flg) {
List<? extends Type1> list = null;
if(flg) {
List<Type1> tmp = new LinkedList<Type1>();
tmp.add(new Type1("a")); tmp.add(new Type1("b")); tmp.add(new Type1("c"));
list = tmp;
} else {
List<Type3> tmp = new LinkedList<Type3>();
tmp.add(new Type3("d", "0", "A"));
tmp.add(new Type3("e", "1", "B"));
tmp.add(new Type3("f", "2", "C"));
list = tmp;
}
return list;
}
/**
* ?をした際、なんでもよいことになってしまうが、それについて制限を付ける方法 その2
* 特定のクラスより下位のクラスは不許可とする制限をつける方法
*/
public List<? super Type2> sampleC() {
//以下はOK
// List<Type1> tmp = new LinkedList<Type1>();
// return tmp;
//以下もOK
List<Type2> tmp1 = new LinkedList<Type2>();
tmp1.add(new Type2("a", "0")); tmp1.add(new Type2("b", "1")); tmp1.add(new Type2("c", "2"));
return tmp1;
//以下はNG
// List<Type3> tmp = new LinkedList<Type3>();
// return tmp;
}
}
|
実行部分 |
/**
* ジェネリクスを使う その1
* ?(ワイルドカード)について
*/
@SuppressWarnings("unchecked")
private void sample1work0() {
GenericSample1 smpl = new GenericSample1();
List<String> listA;
listA = (List<String>)smpl.sampleA("あいうえお");
for(String str : listA)System.out.println("list(String) : "+str);
System.out.println();
List<Integer>listB = (List<Integer>)smpl.sampleA(null);
for(Integer i : listB)System.out.println("list(Integer) : "+i);
}
/**
* ジェネリクスを使う その2
* ?(ワイルドカード)を制限
* 上限境界をもつワイルドカード指定方法(extends)と、下限境界をもつワイルドカード指定方法(super)です。
*/
@SuppressWarnings("unchecked")
private void sample1work1() {
GenericSample1 smpl = new GenericSample1();
List<Type1> list1 = (List<Type1>)smpl.sampleB(true);
for(Type1 t : list1)System.out.println("List<Type1> : "+t.getMsg());
System.out.println();
List<Type3> list2 = (List<Type3>)smpl.sampleB(false);
for(Type3 t : list2)System.out.println("List<Type3> : "+t.getMsg() + "/" + t.getMsg2() + "/" + t.getMsg3());
System.out.println();
List<Type2> list3 = (List<Type2>)smpl.sampleC();
for(Type2 t : list3)System.out.println("List<Type2> : "+t.getMsg() + "/" + t.getMsg2());
}
|
sample1work0メソッドの実行結果 |
list(String) : あいうえお
list(Integer) : 10000000
|
sample1work1メソッドの実行結果 |
List<Type1> : a
List<Type1> : b
List<Type1> : c
List<Type3> : d/0/A
List<Type3> : e/1/B
List<Type3> : f/2/C
List<Type2> : a/0
List<Type2> : b/1
List<Type2> : c/2
|
拡張for文というのが使えるようになったらしい。
ただし、使える対象は限られていて、Iterableインターフェイスを実装したものだけ。使用箇所は微妙なのかも。
IterableSample.java |
/**
* 拡張for文が使える独自クラスのサンプル
* Iterableの実装をすればいいだけらしい。実装しないといけないのはIteratorを返すメソッドだけ。
* Iteratorを返してあげることができればよい様子。
*/
public class IterableSample<T> implements Iterable<T> {
private List<T> list;
public IterableSample(List<T> list) {
this.list = list;
}
public Iterator<T> iterator() {
return list.iterator();
}
}
|
実行部分 |
/**
* 拡張for文の使い方。
*/
private void sample2work0() {
//Iterableインターフェイスを実装しているクラスなら拡張for文が使えるらしい。
//LinkedListクラスなんかも、Iterableインターフェイスを実装しているので使ってみる。
LinkedList<String> list = new LinkedList<String>();
list.add("a"); list.add("b"); list.add("c");
for(String str : list) {
System.out.println("list1 : "+str);
}
//また、Iterableを実装すれば独自のクラスでも拡張for文を利用可能
IterableSample<String> smpl = new IterableSample<String>(list);
for(String str : smpl) {
System.out.println("list2 : "+str);
}
}
|
sample2work0の実行結果 |
list1 : a
list1 : b
list1 : c
list2 : a
list2 : b
list2 : c
|
javaが標準で用意しているアノテーションです。
以下のようにして使うらしい。
AnnotationSample0.java |
/**
* 標準アノテーションについて(java側が用意しているもの)
*
* 以下のものを標準アノテーションというらしいよ?
* Override
* Deprecated
* SuppressWarnings
* これら3つを使ってみる。
*/
public class AnnotationSample0 extends C1 {
/**
* 上位クラスC1のメソッドをオーバーライドする際、@Overrideをつけます。
* オーバーライドになってない場合、知らせてくれます。
* オーバーライドしているつもりがしていなかった・・・という状態を回避できます。
*/
@Override
public void showMsg() {
String msg = "仕方ないね";
System.out.println("AnnotationSample0#showMsg : "+msg);
}
/**
* 非推奨メソッドを表します。
* eclipseなんかだと、対象のメソッドを記述すると取り消し線が出てきます。
*/
@Deprecated
public void showMsg2() {
System.out.println("これってレスリングじゃない・・・!?");
}
/**
* むりくりなキャストなどを行った場合、場合ワーニングがでたりしますが
* それを出力させなくします。
* 指定できるオプションはいくつかあります。
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public void showMsg3() {
List list = new LinkedList();
list.add("aaaaaa");
}
}
class C1 {
public void showMsg() {
System.out.println("C1#showMsg : "+"夏コミにスティック♂ナンバー見に行こうな");
}
}
|
独自でアノテーションを作成することも可能です。
パッケージやクラス、フィールド、ローカル変数などにアノテーションを設定可能です。
ただし、アノテーションデータは動的に変化するものではない様子。あくまでも静的な設定情報な様子です。
アノテーションの作成方法を以下に示します。
今回の例では、クラスに対してアノテーション情報を設定しています。
以下のサンプルを元に、@Targetの値を変え、アノテーションを設定する場所を変えれば、フィールドなどにも同じ方法で設定できると思います。
以下のように作成する。ほとんど、インターフェイスの作成方法とかわらない・・と思う。
SmplAnn.java |
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 独自アノテーションのサンプル その1
*
* @Targetでどこに着けることができるアノテーションか指定します。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SmplAnn {
String val1();
int val2();
Class val3();
SmplEnum val4();
int[] val5();
}
|
SmplEnum.java |
public enum SmplEnum {
VAL1, VAL2, VAL3
}
|
AnnotationSample1.java |
@SmplAnn(val1 = "にゃんにゃん", val2 = 100, val3 = Date.class, val4 = SmplEnum.VAL1, val5 = { 1,2,3,4,5 })
public class AnnotationSample1 {
}
|
実行処理部分 |
private void sample4work0() {
//AnnotationSample1からアノテーションで設定した情報を取得する。
Class cls = AnnotationSample1.class;
Annotation[] ann = cls.getDeclaredAnnotations();
for(int i=0; i<ann.length; i++) {
SmplAnn a = (SmplAnn)ann[i];
System.out.println("val1 : "+a.val1());
System.out.println("val2 : "+a.val2());
System.out.println("val3 : "+a.val3());
System.out.println("val4 : "+a.val4());
int[] arr = a.val5();
for(int v : arr)System.out.println("val5 : "+v);
}
}
|
sample4work0の実行結果 |
val1 : にゃんにゃん
val2 : 100
val3 : class java.util.Date
val4 : VAL1
val5 : 1
val5 : 2
val5 : 3
val5 : 4
val5 : 5
|
■自作するアノテーションに対し、どこへアノテーションを追加可能なのか指定する際の設定可能なもの一覧
@Targetでどこにこのアノテーションをつけることができるのか指定する
複数指定可能。
指定可能なオプション
詳細はこちら参照 → http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/annotation/ElementType.html
指定するオプション | 意味(指定可能な場所) |
ElementType.TYPE | クラス、インタフェース (注釈型を含む)、または emum |
ElementType.FIELD | フィールド |
ElementType.METHOD | メソッド |
ElementType.PARAMETER | パラメータ |
ElementType.CONSTRUCTOR | コンストラクタ |
ElementType.LOCAL_VARIABLE | ローカル変数 |
ElementType.ANNOTATION_TYPE | 注釈型(アノテーション) |
ElementType.PACKAGE | パッケージ |
複数指定した場合の例 : @Target( { ElementType.FIELD, ElementType.METHHOD } )
■定義したアノテーションに対し、そのアノテーション情報をclassファイルに対して残すか残さないか・・・の指定方法
@Retentionで、指定したアノテーションをclassファイルへ残すか残さないか指定する。
指定可能オプション
詳細はこちら参照 → http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/annotation/RetentionPolicy.html
指定するオプション | 意味 |
RetentionPolicy.CLASS | classファイルにはアノテーション情報は記述されるが、実行時、これらの情報は読み取られない(?) 用途がわからんw(バイトコートの静的解析がしたいとき? |
RetentionPolicy.RUNTIME | 普通はこれなはず。これを指定すれば、実行時、アノテーションの情報が取得できる。 |
RetentionPolicy.SOURCE | コンパイラによって注釈が破棄される。 私には用途が思いつかなかった。。。 |