Можно ли в Java запретить определенные суперклассы в дженериках? - PullRequest
1 голос
/ 11 июня 2019

Я хотел бы спросить, возможно ли в Java 8+ объявить общую границу T, чтобы она расширяла суперкласс / суперинтерфейс U (который может быть Object или Serializable), но ломается компиляция , если T расширяется L (который должен сначала расширяться U).

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

Я покажу свой пример в очень упрощенном виде. Речь идет о таблицах и динамических фильтрах.

#Displays a text "[field name] is equal to [value]"
#Value (T) must be Oject
#Internally uses Object::toString
#Null shows blank string
public static String <T> localizeEq(Localizable fieldName, T value);
       <LocalDate> localize(forI18nLabel("DATE_OF_BIRTH_LABEL",dateOfBirth)
       "Date of birth equals 01/01/1900" (en)
       "syntymäaika on 01/01/1990" (fi)

#Additional diplays for "ge, gte, le..."
#Overload methods not displayed

#SimpleFilter is {op:"ge|ge|eq...",value:""}}
#The effective display depends on the op attribute
#Example "[field name] is [operator] [value]"
#Example "[field name] is less or equal than [upper]"
#If <filter != null but filter.op == null || filter.value> the method returns null
public static String <T> localize(Localizable fieldName, SimpleFilter<T> filter)
   #localize(forI18nLabel("SALARY"),salaryFilter)
   #salaryFilter = {op:"lt",value:10000}
   #Salary is less than 10000 (en)

Теперь проблема в том, что верхняя граница U моих обобщенных элементов равна Serializable, и разработчик случайно вызвал localizeEq, который принимает атомарные значения, с параметром типа SimpleFilter<?>, который расширяется Serializable. Метод localizeEq создает текст фильтра "[имя поля] равно {op: null, value: null}".

Основная проблема - проверка нуля. Методы, которые работают с атомарными значениями (например, localizeEq, localizeNe), проверяют, является ли параметр нулевым. Методы, работающие со сложными фильтрами, проверяют, что либо параметр фильтра равен нулю , либо , но его значение равно нулю, прежде чем продолжить.

В этом причина вопроса. Очевидно, я могу (исправлю мой код для того, чтобы) проверить тип параметра value при вызове метода, но имеет три недостатка:

  • Разработчики находят его только во время выполнения
  • Разработчики обнаруживают проблему только тогда, когда value не равно нулю
  • Никто в моей компании не проводит автоматические тесты, поэтому они узнают об этом только при запуске всего приложения и установке фильтра в ненулевое значение. После завершения ручного теста оно никогда не повторяется

[Изменить] Для моего конкретного случая есть еще одна хитрость, но она включает в себя создание более десятка перегруженных устаревших методов:

@Deprecated
public static String localize[Eq|Ne...](Localizable fieldName, SimpleFilter<?> value){ throw new UnsupportedOperationException("Wrong method");}

[Редактировать 3]

Код включен Суть . Обратите внимание, что в коде репозитория мы статически импортируем методы SimpleFilter.filter или LocalDateRangeFilter.filter. В вопросе предполагается, что localize(Localizable,SimpleFilter) является частью того же класса, что и другие методы. И обратите внимание, что в нашем репозитории есть несколько других классов * RangeFilter, которые поддерживают Joda Time, Java Util Date и NumericRange. Все они страдают от одной и той же проблемы.

В любом случае я хотел бы сосредоточиться на объеме вопроса: запрещение расширения в общем виде, что в JLS кажется невозможным.

1 Ответ

0 голосов
/ 11 июня 2019

Я хотел бы спросить, можно ли в Java 8+ объявить общую границу T, чтобы она расширяла суперкласс / суперинтерфейс U (который может быть Object или Serializable)но прерывает компиляцию, если T расширяет L (который должен сначала расширяться U).

T в вашем псевдокоде является параметром типа, а не границей.Границы - это что-то другое, и на самом деле, ограничение T кажется тем, о чем вы спрашиваете.Действительно, без одного - в частности, без нижнего связанного - ваш localizeEq() метод ничего не получает от того, что он является универсальным.В нынешнем виде этот метод будет понятнее, если вы просто избавитесь от T и объявите второй параметр типа Object (который будет эквивалентен текущему коду) или Serializable или как угодно.

Я полагаю, что метод был сделан универсальным в надежде каким-то образом использовать его параметр типа для исключения аргументов определенных подтипов, но это невозможно в Java, потому что

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

Теперь проблема в том, что верхняя граница U моих обобщенных элементов равна Serializable, и разработчик случайно вызвал localizeEq, который принимает атомарные значения, с параметром типа SimpleFilter<?>, который расширяет Serializable.Метод localizeEq создает текст фильтра "[имя поля] равно {op: null, value: null}".

Если не предполагается передать SimpleFilter вlocalizedEq() метод, тогда я бы сказал, что у вас есть недостаток дизайна здесь.Конечно, вы можете обнаружить нарушения в run time, но система типов не предоставляет способ выразить ограничение времени компиляции, которое вы ищете.

Для моегоВ конкретном случае есть еще одна хитрость, но она включает в себя создание более десятка перегруженных устаревших методов:

Действительно, перегрузка, вероятно, является наилучшим доступным решением, но я бы предложил подходить к нему с другой стороны.Вместо добавления перегрузок для localizeEq, localizeNe, и т. Д. , устарели существующих версий этих методов и вместо этого перегружают localize версией или версиями, которые предоставляют требуемыеповедение аргументов, которые не являются SimpleFilter s.

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