Параметры типа java generics и операции с этими типами
В поисках ответа на интересную ситуацию, с которой я недавно столкнулся, я столкнулся со следующим вопросом: Тип безопасности, генерические файлы Java и запросы
Я написал следующий класс (очищенный бит)
public abstract class BaseDaoImpl<T extends Serializable> extends HibernateDaoSupport implements BaseDao<T> {
/**
* Finds and Returns a list of persistent objects by a collection of criterions
* @param criterions
* @return list of persistent objects
* @throws DBException
*/
@SuppressWarnings("unchecked")
protected List<T> findByCriteria(Collection<Criterion> criterions) throws DBException {
try {
DetachedCriteria criteria = DetachedCriteria.forClass(T.class); // BAD!!!
for (Criterion criterion : criterions) {
criteria.add(criterion);
}
List<T> result = getHibernateTemplate().findByCriteria(criteria);
return result;
}
catch (Exception e) {
throw new DBException(T.class + " lookup by " + criterions + " failed", e); // BAD!!!
}
}
}
Для некоторых (возможно, веских причин) T.class
вызывает ошибку времени компиляции.
Мой первый вопрос: почему?
Если я изменил его на T.getClass()
, который, очевидно, не должен компилироваться, потому что no 'T', когда "расширен" или проходит через "erasure", должен иметь статический метод, такой как. Eclipse IDE предоставляет следующее компиляционное сообщение:
Невозможно сделать статическую ссылку на нестатический метод getClass() из Тип объекта
Мой второй вопрос - почему? И что означает эта ошибка?
Наконец, было бы наиболее оптимальным решение для этого, как указано выше в ссылке (точнее, моей интерпретации)?
public abstract class BaseDaoImpl<T extends Serializable> extends HibernateDaoSupport implements BaseDao<T>, MyGenericHelper<T> {
/**
* Finds and Returns a list of persistent objects by a collection of criterions
* @param criterions
* @return list of persistent objects
* @throws DBException
*/
@SuppressWarnings("unchecked")
protected List<T> findByCriteria(Collection<MyCriterion> criterions) throws DBException {
try {
DetachedCriteria criteria = DetachedCriteria.forClass(getGenericClass()); // BAD!!!
for (Criterion criterion : criterions) {
criteria.add(criterion);
}
List<T> result = getHibernateTemplate().findByCriteria(criteria);
return result;
}
catch (Exception e) {
throw new DBException(getGenericClass() + " lookup by " + criterions + " failed", e); // BAD!!!
}
}
}
public interface MyGenericHelper<T extends Serializable> {
public Class<T> getGenericClass();
}
Спасибо!
Ответы
Ответ 1
Причина, по которой T.class недоступна, заключается в том, что T стирается во время компиляции, поэтому во время выполнения он не существует, чтобы получить класс.
Типичный взлом вокруг этого заключается в создании метода factory:
public static <T extends Serializable> BaseDAOImpl<T> createBaseDAO(Class<T> klass) {
return new BaseDAOImpl<T>(klass);
}
И затем в конструкторе сохраните переменную klass в качестве поля и укажите ее, когда вам это нужно.
Вы можете использовать свой интерфейс (лично я бы просто пошел с защищенным абстрактным методом), если вы хотите сохранить конструктор no-arg.
protected abstract Class<T> getGenericClass();
EDIT: В случае общего абстрактного суперкласса есть несколько вариантов. У одного есть конструктор, а затем подклассы просто должны его вызвать (не имея конструктора no-arg). Что-то вроде этого:
protected BaseDAOImpl(Class<T> klass) {
//store the parameter in a field.
}
Тогда статический метод не имеет значения, так как вам нужно создать подкласс. Статический метод factory больше используется, когда на основе класса вы можете вернуть правильную реализацию (так что у вас есть factory, а не только стратегия).
Для полноты я должен указать, что если подклассы объявляют общий, когда они расширяют абстрактный класс следующим образом:
public IntegerImpl extends BaseDAOImpl<Integer> {}
тогда общий тип сохраняется в классе. Существует действительно уродливый взлом, чтобы получить это, используя класс. Я экспериментировал с этим, и он работал:
(Class<?>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]
Но он делает огромные предположения о иерархии наследования и не должен использоваться ни в чем серьезном, если его вообще можно избежать, но я включаю его для полной картины того, что происходит.
Ответ 2
Для некоторых (вероятно, серьезных причин) T.class вызывает ошибку времени компиляции.
Мой первый вопрос: почему?
В момент компиляции компилятор не знает тип T (в этом случае он может быть Integer, String), T.class не может быть поддержан, чтобы вернуть фактический класс.
Однако во время выполнения информация типа удаляется из-за типа стирания
Cannot make a static reference to the non-static method getClass() from
тип Object
Мой второй вопрос - почему? И что действительно ли эта ошибка означает?
Hm.. getClass() - метод класса (нестатический), для которого требуется объект. T не является объектом, это тип, поэтому он терпит неудачу. Они не статичны, потому что уже есть ключевое слово XXX.class, из которого вы можете получить объект Class.