Java: Instanceof и Generics

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

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

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

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

Невозможно выполнить проверку экземпляра с параметром типа E. Используйте вместо этого объект стирания, поскольку информация об универсальном типе будет удалена во время выполнения

Каков лучший способ сделать это?

Ответы

Ответ 1

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

Вы можете поймать его, создав factory для вашего объекта следующим образом:

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

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

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

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

Ответ 3

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

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

В вышеприведенном случае во время выполнения вы получите String.class из getParameterizedClass(), и он кэшируется, поэтому вы не получите никаких накладных расходов при нескольких проверках. Обратите внимание, что вы можете получить другие параметризованные типы по индексу из метода ParameterizedType.getActualTypeArguments().

Ответ 4

Старый пост, но простой способ выполнить проверку экземпляров instanceOf.

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

Ответ 5

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

Мой probem находился внутри абстрактного класса, который реализует Observer. Метод Observable fires update (...) с классом Object, который может быть любым объектом 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){
  }
}

Ответ 6

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

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

Ответ 7

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

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

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

Ответ 8

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

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