ジェネリクスなリストから配列に変換する

以前に日記でも採り上げたことのある、Predicateを使うFindAllのJava版だがAndroid環境に移した所、コンパイルが通らない。

  • findAll (Java6版)
public interface IPredicate {
    boolean evaluate(T input);
}

public static final  T findAll(final T array, IPredicate match) {
    ArrayList temp = new ArrayList();
    for (T t : array) {
        if ( match.evaluate(t) ){
            temp.add(t);
        }
    }
    
    T[] result = Arrays.copyOf(array, 0); //×
    return temp.toArray(result);
}

AndroidJavaはJava SE5相当だが、Arrays.copyOf(T[]〜)はJava6からサポートのため使えないのだ。

述語論理を使ったファインダはコレクションに対する操作ロジックの見通しが良く、条件判断も最小限に書けるため、便利で一度使うと病みつきになる。

Field[] fields = FinderUtil.findAll(activity.getClass().getDeclaredFields(), 
    new IPredicate(){
        @Override
        public boolean evaluate(Field input) {
            return (input.isAnnotationPresent(BindView.class));
        }});
for ( Field field : fields ) {
    〜
}

Androidでもそのまま使いたいし、できればシグネチャを変えたくない。

結局は要素の型パラメタを追加せずに済ませることができたが、キャスト無しで実装することは出来ず、またしても@SuppressWarningsの世話になることに。

@SuppressWarnings("unchecked")
public static final  T findAll(T array, IPredicate match) {
    ArrayList temp = new ArrayList();
    for (T t : array) {
        if ( match.evaluate(t) ){
            temp.add(t);
        }
    }
    
    T result = (T)Array.newInstance(array.getClass().getComponentType(), temp.size());
    return temp.toArray(result);
}

考えてから気がついたんだが、Java6のArray.copyOfのソースコードを読んで、同じ処理を書くだけでいいじゃん。

  • Java6のArray.javaから抜粋引用
@SuppressWarnings("unchecked")
public static  T copyOf(T original, int newLength) {
    return (T) copyOf(original, newLength, original.getClass());
}

@SuppressWarnings("unchecked")
public static  T copyOf(U original, int newLength, Class> newType) {
    T copy = ((Object)newType == (Object)Object.class)
        ? (T) new Object[newLength]
        : (T) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

これを呼びだせば、findAllの@SuppressWarningsは取り除くことができる。(結局、キャストとSystem.arraycopyを使っているんだね)
@SuppressWarningsから縁を切ることはできないが、影響が出る部分を局所化するってことはできる訳だ。