Каков наилучший способ справиться с потенциальными исключениями времени выполнения из «неконтролируемого преобразования» Java? - PullRequest
4 голосов
/ 13 февраля 2012

Итак, у меня есть функция, которая выглядит следующим образом:

@SuppressWarnings("unchecked")
public static <E> Set<E> getSetOfClass(Query q,Class<E> clazz) {
    return new LinkedHashSet<E>( q.getResultList() );
}

Я полагаю, что для этого нужно взять javax.persistence.Query и вернуть его набор результатов как общий Set<Class>.

Это кажется отличным решением, но, во-первых, действительно ли он делает то, о чем я думаю, и это лучший способ достичь этого?Я нахожу странным, что я никогда не ссылаюсь на мой clazz параметр, но он, кажется, делает то, что я хочу.

Во-вторых, если это все правильно и разумно, какие ошибки это может вызвать?Я бы подумал, если бы я дал ему неправильный класс, эта функция не сработала бы, хотя я совсем не уверен в этом.

Если я сделаю что-то вроде:

Query q = em.createQuery("FROM Element");
Set<Fish> s = MyUtil.getSetOfClass( q, Fish.class);

И если Fish не является суперклассом Element, то что происходит?Должен ли я считать, что эта функция всегда будет использоваться правильно, или я должен делать это для обработки ошибок?Каковы рекомендации людей о наилучшей практике?

С уважением,

Глен х

Ответы [ 3 ]

3 голосов
/ 13 февраля 2012

getSetOfClass не гарантирует, что все элементы в наборе являются объектами типа E.Если вы называете это неправильно (что вы всегда можете), например:

 Set<Cat> cats = getSetOfClass(findAllFish(), Cat.class);

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

Я бы добавил несколько проверокв открытый метод getSetOfClass, чтобы гарантировать, что содержимое набора соответствует параметру типа:

@SuppressWarnings("unchecked")
public static <E> Set<E> getSetOfClass(Query q,Class<E> clazz) {
   return getSetOfClass(Query q,Class<E> clazz, true);
}

@SuppressWarnings("unchecked")
public static <E> Set<E> getSetOfClass(Query q,Class<E> clazz, boolean validate) {
    List result = q.getResultList();
    if (validate) {
       for (Object o:result) {
          if(!o.getClass().equals(clazz)) {
              // handle error
          }
       }
    }
    return new LinkedHashSet<E>( result );
}
2 голосов
/ 13 февраля 2012

Чтобы добавить к ответу @ Andreas_D, просто помните, что вся информация об универсальных элементах Java используется только для проверки вашего кода на корректность типов во время компиляции и стирается во время выполнения.Следовательно, фактически вы получаете что-то вроде этого:

public static Set<Object> getSetOfClass(Query q,Class<Object> clazz) {
  return new LinkedHashSet<Object>( q.getResultList() );
}

Что означает, что во время выполнения все будет работать просто так, как описано выше.
Обновление: Как любезноКак указал @Blaisorblade, метод getSetOfClass может использовать clazz для проверки правильности типа и быстрого сбоя, если тип неправильный.Хотя это не может быть сделано во время компиляции, было бы легче точно определить проблему в случае сбоя во время выполнения.

Теперь предположим, что позже у вас есть:

Query q = em.createQuery("FROM Element");
Set<Fish> s = MyUtil.getSetOfClass( q, Fish.class);
for(Fish fish : s){
  fish.swim();
}

Тогда во время выполнения это будет выглядеть так:

Query q = em.createQuery("FROM Element");
Set<Object> s = MyUtil.getSetOfClass( q, Fish.class);
for(Object fish : s){
  ((Fish)fish).swim();
}

Теперь вы можете видеть, что произойдет, если элементы имеют тип Cat.Часть (Fish)fish выдаст ClassCastException (если он зайдет так далеко).

Обобщение действительно очень полезно, когда информация о типе может отслеживаться компилятором без каких-либо предупреждений unchecked от началаконец.В других случаях (например, у вас), когда генерики «взломаны» посередине, они не могут гарантировать правильность вашей программы.Это неизбежно, особенно в тех случаях, когда данные сохраняются на диске или в базе данных, поскольку невозможно гарантировать, что сохраненные данные имеют правильный тип.Программист должен быть просто осторожен.

1 голос
/ 13 февраля 2012

Я никогда не был уверен, нравится ли мне дженерики или нет. В этом случае кажется, что они были бы хорошей идеей и избавили бы вас от многих проблем. Поскольку постоянство (пока), кажется, не поддерживает их, я надену шляпу против обобщений и объясню, как выполняется real Java-программирование.

Тогда я бы хотел отказаться от обобщений и классов и просто вернуть старый набор:

public static Set getSetOfClass( Query q )  {
    return new LinkedHashSet( q.getResultList() );
}

(используйте @SuppressWarnings ("unchecked") по мере необходимости, если вы не можете получить компилятор 1.4.)

Если запрос не содержит ничего, кроме E, у вас никогда не будет проблем. Или, во всяком случае, проблема будет редкой, очевидной во время выполнения и лучше всего решаемой программистами, которые неправильно используют ваш метод. Ничто не говорит «Измените свой базовый подход», как неожиданное исключение ClassCastException во время выполнения.

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

Если вы действительно знаете их цели, то, возможно, вы можете избавить их от некоторых проблем, добавив параметр Clazz, чтобы вы знали, чего они хотят. Затем вы можете выполнить фильтрацию, либо вернуть нулевое значение, либо выдать собственное проверенное исключение, либо вернуть специальный объект класса, который подробно объясняет природу содержимого Set. Но убедитесь, что вы не выполняете больше работы, чем сохраняете пользователей метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...