Java: Экземпляр и Обобщения

135

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

Но Затмение жалуется, когда я делаю это:

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

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

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

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

Ник Хейнер
источник

Ответы:

79

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

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

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

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

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }
Ишай
источник
3
Я не думаю, что ты хочешь Class.isAssignableFrom.
Том Хотин - Tackline
@ Том, я написал это прошлой ночью по памяти, и я исправил это, чтобы фактически пропустить урок (да!), Но в противном случае, я не понимаю, почему ты не хочешь этого (может быть, мне нужно больше кофе этим утром, я ' м только на моей первой чашке).
Ишай
Я с Томом. Не могли бы вы уточнить это? Использование isAssignableFrom () было бы моим выбором для работы. Может я что-то упустил?
luis.espinal
6
@luis, комментарий Тома, вероятно, означал, что я должен использовать isInstance () и передать фактический параметр arg0. Преимущество состоит в том, что вы избегаете нулевой проверки.
Ишай
1
У меня похожая ситуация, но я не могу полностью понять ответ. Можете ли вы уточнить это лучше для манекена, как я?
Рубенс Мариуццо
40

Два варианта проверки типов во время выполнения с помощью обобщений:

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

SharkAlley
источник
18

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

public static <T> boolean isInstanceOf(Class<T> clazz, Class<T> targetClass) {
    return clazz.isInstance(targetClass);
}
Йонас Педерсен
источник
21
Неясно, что вы на самом деле вносите здесь.
Андрей
12

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

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 ().

terryscotttaylor
источник
7

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

Мой тест был внутри абстрактного класса, который реализует Observer. Обновление метода Observable запускает (...) классом 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){
  }
}
hsaturn
источник
5

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

public int indexOf(Object arg0){
  try{
    E test=(E)arg0;
    return doStuff(test);
  }catch(ClassCastException e){
    return -1;
  }
}
Бен
источник
1
+1 Прагматичное неповторимое решение для простой проверки! Хотелось бы, чтобы я проголосовал за это больше
higuaro
24
Это не сработает. E стирается во время выполнения, поэтому приведение не будет неудачным, вы просто получите предупреждение компилятора об этом.
Ишай
1

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

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

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

Джейсон С
источник
да, интерфейс List требует, чтобы функция принимала параметр объекта.
Ник Хейнер
вы реализуете список? Почему вы не используете List <E>?
Джейсон С
(см., например, объявление ArrayList: java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html )
Джейсон С.
@Rosarch: метод indexOf () интерфейса List не требует, чтобы заданный аргумент был того же типа, что и искомый объект. это просто должен быть .equals () для него, а объекты разных типов могут быть .equals () друг для друга. Это та же проблема, что и для метода remove (), см .: stackoverflow.com/questions/104799/…
newacct
1
[[[смущенно]]] не берите в голову, я думал, что indexOf () требует E как параметр, а не Object. (почему они это сделали?! ??!)
Джейсон С
1

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

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;
             }
         }}
     );
Том Хотин - Tackline
источник
(Хотя вы можете захотеть сделать проверку типа экземпляра, если вы используете Comparatorили похожий.)
Том Хотин - tackline