JAVAのジェネリクスではジェネリック型の配列は作れないけど、何となく作りたい場面があった。
良くあるのはObject型で作っておいて、後で返すときとかにキャストという方法。でも嫌ジャン。
T t = (T) new Object[obj.length];
で、一見T型の配列が出来た様に見えるけど、tのクラスを見たら[L java.lang.Object型のオブジェクトだもん。
ということは、自分か自分のスーパークラスにしかキャストできないからTがString型だったとしても、String s = (String)t;ということは出来ない。じぇんじぇん使えない。
(ちなみに、T t =(T) new Object[obj.length]が成立するのは、コンパイルすると結局のところObject t=(Object)new Object[obj.length]と書き換わり、矛盾が生じないからだ。)
つーわけで、ちゃんと[L T型のオブジェクトになって欲しい。
というわけで、何となくやってみたらそれっぽい物が出来た。
public class GTest {
public static void main(String args) {
String s = {};
System.out.println(generev(s));
}
("unchecked")
public static <T> T generev(T obj){
if(obj==null)return null;
T t = (T) Array.newInstance(obj.getClass().getComponentType(), obj.length);
for(int i=0;i < obj.length;i++){
t[t.length-i-1] = obj[i];
}
return t;
}
}
uncheckedしてるけど、Array.newInstanceで作成されるオブジェクトは[L T型で、TはObjectと実行時は処理されるので安全です。
完全に勘違いしていました。newInstanceで返ってくるオブジェクトはObject型で返ってくるので、Object型の配列にするにしてもキャストする必要があります。無駄ではありません。
でも、これは完璧じゃないんだよね。
例えば
Integer i ={1,2};
Number n =GTest.<Number>generev(i);
と書いても、返ってくる配列はNumber型じゃなくてInteger型になってしまう。Tという物がコンパイルするとObjectと同義になる以上、これは解決出来ないと思うけど。というか、解決出来ないからTを作成する文法がないわけだけど。
クロージャーとか良いからこの辺何とかしてくれねーかな。
ちなみに、どーでもいいけど最近C言語とかの配列のint a[ ]的な が後ろにあるのがどーも気持ち悪い。
実態は全然違うのに、まるでaはint型の変数みたいに見えるじゃん?今回みたいにJAVAなら[L ~型のオブジェクトだし。まぁ、気持ち悪いだけで、別に何の問題もないけど……
追記的なあれ書いておきました