Java: Instanceof и дженерики

прежде чем я просмотрю свою общую структуру данных для индекса значения, я хотел бы увидеть, является ли это даже экземпляром типа this был параметризован.

но Eclipse жалуется, когда я это делаю:

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

это сообщение об ошибке:

не удается выполнить проверку instanceof против параметра типа E. используйте вместо него объект erasure, так как общие сведения о типе будут удалены во время выполнения

что лучше как это сделать?

8 ответов


сообщение об ошибке все сказано. Во время выполнения тип исчез,нет способа проверить его.

вы можете поймать его, сделав фабрику для вашего объекта, как это:

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

а затем в конструкторе объекта сохраните этот тип, так что переменная, чтобы ваш метод мог выглядеть так:

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

два варианта проверки типа среды выполнения с помощью дженериков:

Вариант 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: вы получаете бесплатную проверку null при использовании instanceof. Поскольку вы не можете использовать его, вам может потребоваться проверить значение null голыми руками.


при условии, что ваш класс расширяет класс с общим параметром, вы также можете получить это во время выполнения через отражение, а затем использовать это для сравнения, т. е.

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 (), и он кэширует, поэтому вы не получаете никаких накладных расходов на отражение при нескольких проверках. Обратите внимание, что другие параметризованные типы можно получить по индексу из ParameterizedType.метод getActualTypeArguments ().


старый пост, но простой способ сделать общий instanceOf проверки.

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

у меня была та же проблема, и вот мое решение (очень скромное, @george: на этот раз компиляция и работа ...).

мой probem был внутри абстрактного класса, который реализует Observer. Обновление метода наблюдаемых пожаров(...) с классом Object, который может быть любым типом объекта.

Я хочу только обрабатывать объекты типа 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){
  }
}

или вы можете поймать неудачную попытку бросить в E например.

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

технически вам не нужно, это точка дженериков, поэтому вы можете выполнить проверку типа компиляции:

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

но тогда @ Override может быть проблемой, если у вас есть иерархия классов. В противном случае см. ответ Yishai.


тип среды выполнения объекта является относительно произвольным условием для фильтрации. Я предлагаю оставить такой muckiness от вашей коллекции. Это просто достигается путем передачи делегата коллекции фильтру в конструкции.

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;
             }
         }}
     );