Java: Instanceof и дженерики
прежде чем я просмотрю свою общую структуру данных для индекса значения, я хотел бы увидеть, является ли он даже экземпляром типа this
был параметризован.
но затмение жалуется, когда я делаю это:
@Override
public int indexOf(Object arg0) {
if (!(arg0 instanceof E)) {
return -1;
}
это сообщение об ошибке:
невозможно выполнить проверку instanceof по параметру типа E. вместо этого используйте его объект стирания, так как информация об универсальном типе будет удалена во время выполнения
что лучше как это сделать?
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. Обновление метода наблюдаемых пожаров(...) с классом объекта, который может быть любым типом объекта.
Я хочу только обрабатывать объекты типа 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 может быть проблемой, если у вас есть иерархия классов. В противном случае см. ответ Ишая.
тип времени выполнения объекта является относительно произвольным условием для фильтрации. Я предлагаю держать такую гадость подальше от вашей коллекции. Это просто достигается путем делегирования вашей коллекции фильтру, переданному в конструкцию.
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; } }} );