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


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


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

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

(Хотя вы можете захотеть сделать проверку типа экземпляра, если вы используете Comparatorили похожий.)
Том Хотин - tackline
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.