web-dev-qa-db-ja.com

Java:InstanceofおよびGenerics

値のインデックスの一般的なデータ構造を調べる前に、this型のインスタンスがパラメータ化されているかどうかを確認したいと思います。

しかし、Eclipseは私がこれを行うと文句を言います:

@Override
public int indexOf(Object arg0) {
    if (!(arg0 instanceof E)) {
        return -1;
    }

これはエラーメッセージです。

型パラメーターEに対してinstanceofチェックを実行できません。代わりにその消去オブジェクトを使用してください。一般的な型情報は実行時に消去されるためです。

それを行うためのより良い方法は何ですか?

121
Nick Heiner

エラーメッセージにすべてが記載されています。実行時に、タイプはなくなります。チェックする方法はありません。

次のようにオブジェクトのファクトリを作成することで、キャッチできます。

 public static <T> MyObject<T> createMyObject(Class<T> type) {
    return new MyObject<T>(type);
 }

そして、オブジェクトのコンストラクターにその型を格納します。そのため、メソッドが次のようになるように変数を変更します。

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }
74
Yishai

ジェネリックを使用したランタイムタイプチェックの2つのオプション:

オプション1-コンストラクターの破損

IndexOf(...)をオーバーライドしていて、パフォーマンスだけのために型をチェックして、コレクション全体を反復するのを節約したいとします。

このような不潔なコンストラクタを作成します。

public MyCollection<T>(Class<T> t) {

    this.t = t;
}

その後、 isAssignableFrom を使用してタイプを確認できます。

public int indexOf(Object o) {

    if (
        o != null &&

        !t.isAssignableFrom(o.getClass())

    ) return -1;

//...

オブジェクトをインスタンス化するたびに、自分自身を繰り返す必要があります。

new MyCollection<Apples>(Apples.class);

あなたはそれが価値がないと決めるかもしれません。 ArrayList.indexOf(...) の実装では、タイプが一致することを確認しません。

オプション2-失敗させます

不明な型を必要とする抽象メソッドを使用する必要がある場合、本当に必要なのは、コンパイラーが泣くのをやめることだけですinstanceof。このようなメソッドがある場合:

protected abstract void abstractMethod(T element);

次のように使用できます。

public int indexOf(Object o) {

    try {

        abstractMethod((T) o);

    } catch (ClassCastException e) {

//...

コンパイラをだますために、オブジェクトをT(ジェネリック型)にキャストしています。 実行時にキャストは何もしません 、しかし、間違ったタイプのオブジェクトを抽象メソッドに渡そうとすると、ClassCastExceptionが発生します。

注1:抽象メソッドで追加の未チェックキャストを実行している場合、ClassCastExceptionsはここでキャッチされます。それは良いことも悪いこともあるので、よく考えてください。

注2: instanceofを使用すると、無料のnullチェックを取得します 。使用できないため、素手でnullをチェックする必要がある場合があります。

37
SharkAlley

古い投稿ですが、汎用のinstanceOfチェックを行う簡単な方法です。

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}
14
Jonas Pedersen

クラスがジェネリックパラメーターでクラスを拡張している場合は、実行時にリフレクションを介して取得し、比較に使用することもできます。

class YourClass extends SomeOtherClass<String>
{

   private Class<?> clazz;

   public Class<?> getParameterizedClass()
   {
      if(clazz == null)
      {
         ParameterizedType pt = (ParameterizedType)this.getClass().getGenericSuperclass();
          clazz = (Class<?>)pt.getActualTypeArguments()[0];
       }
       return clazz;
    }
}

上記の場合、実行時にgetParameterizedClass()からString.classを取得し、複数のチェックでリフレクションのオーバーヘッドが発生しないようにキャッシュします。 ParameterizedType.getActualTypeArguments()メソッドからインデックスによって他のパラメーター化された型を取得できることに注意してください。

12

私は同じ問題を抱えていましたが、ここに私の解決策があります(非常に謙虚、@ george:今回はコンパイルと作業...)。

私のプロベムは、オブザーバーを実装する抽象クラスの中にありました。 Observableは、任意の種類のオブジェクトにできるObjectクラスでupdate(...)メソッドを起動します。

タイプTのオブジェクトのみを処理したい

解決策は、実行時に型を比較できるように、クラスをコンストラクターに渡すことです。

public abstract class AbstractOne<T> implements Observer {

  private Class<T> tClass;
    public AbstractOne(Class<T> clazz) {
    tClass = clazz;
  }

  @Override
  public void update(Observable o, Object arg) {
    if (tClass.isInstance(arg)) {
      // Here I am, arg has the type T
      foo((T) arg);
    }
  }

  public abstract foo(T t);

}

実装のために、コンストラクタにクラスを渡すだけです

public class OneImpl extends AbstractOne<Rule> {
  public OneImpl() {
    super(Rule.class);
  }

  @Override
  public void foo(Rule t){
  }
}
7
hsaturn

または、Eにキャストしようとして失敗したことをキャッチできますeg。

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}
5
ben

技術的には必要ないはずです、それがジェネリックのポイントなので、コンパイル型チェックを行うことができます:

public int indexOf(E arg0) {
   ...
}

ただし、クラス階層がある場合は、@ Overrideが問題になる可能性があります。それ以外の場合は、Yishaiの答えを参照してください。

1
Jason S

オブジェクトの実行時タイプは、フィルタリングするための比較的任意の条件です。そのような卑劣さをあなたのコレクションから遠ざけることをお勧めします。これは、コレクションでデリゲートをフィルターに委任することで簡単に実現できます。

public interface FilterObject {
     boolean isAllowed(Object obj);
}

public class FilterOptimizedList<E> implements List<E> {
     private final FilterObject filter;
     ...
     public FilterOptimizedList(FilterObject filter) {
         if (filter == null) {
             throw NullPointerException();
         }
         this.filter = filter;
     }
     ...
     public int indexOf(Object obj) {
         if (!filter.isAllows(obj)) {
              return -1;
         }
         ...
     }
     ...
}

     final List<String> longStrs = new FilterOptimizedList<String>(
         new FilterObject() { public boolean isAllowed(Object obj) {
             if (obj == null) {
                 return true;
             } else if (obj instanceof String) {
                 String str = (String)str;
                 return str.length() > = 4;
             } else {
                 return false;
             }
         }}
     );