Избегайте создания общих классов с помощью непроверенных приведений типов
Допустим, я создаю пакет process
с публичным классом Processor
, который имеет конструктор, который ожидает параметр типа Container<I>
, Класс Container<I>
лежит в другом пакете вне моего контроля и содержит объекты произвольного типа I
, Контейнер с содержащимися элементами передается между несколькими классами пакета process
,
Элементы обрабатываются с помощью методов интерфейса Processable
, Они могут быть получены пользователем process
через метод getProcessedItem(Property property)
, Этот метод возвращает обработанный элемент со свойством property
который гарантированно содержится в переданном контейнере и поэтому может быть приведен к параметру типа контейнера.
пример класса:
package process;
import container.Container;
public class Processor<I extends Processable> {
private final ItemProcessor itemProcessor;
public Processor(Container<I> container){
this.itemProcessor = new ItemProcessor(container);
}
@SuppressWarnings("unchecked")
public I getProcessedItem(Property property){
return (I)itemProcessor.getProcessedItem(property);
}
}
Как правило, в такой ситуации неплохо создавать неконтролируемое приведение типов?
Единственная разумная альтернатива, которую я до сих пор выяснил, - сделать все классы в пакете process
которые должны содержать ссылку на параметризованный контейнер (например, ItemProcessor
) также является общим для сохранения информации о типе, необходимой для getProcessedItem(Property property)
,
РЕДАКТИРОВАТЬ: Чтобы уточнить функциональность класса контейнера в моем примере:
Я хотел, чтобы контейнерный класс представлял некоторый довольно примитивный подобный коллекции класс, который должен просто позволить процессору извлекать элементы, которые он должен обработать.
Это означает ItemProcessor
и классы, используемые ItemProcessor
должен иметь возможность обрабатывать обрабатываемые элементы (после их извлечения из контейнера) так, как им нравится (например, сохранять их в списках или других структурах данных). И, наконец, должна быть возможность запрашивать обработанный элемент на основе определенного свойства процессора. Например:
getProcessedItem(Property.MOST_RECENTLY_PROCESSED)
Как стандартные классы коллекции Java (например, ArrayList
) Container<I>
не гарантирует строго, что он содержит только I
и его потомки. Это означает, что когда-то контейнер используется как Processable
в то время как на самом деле не будучи Processable
происходит исключение приведения.
В приведенной выше версии класса Processor исключение не генерируется сразу, когда элемент запрашивается через getProcessedItem
не совместим с типом I
(из-за непроверенного актерского состава). Но также, если я сделал ItemProcessor
общий (ItemProcessor<I>
) и сделал свое getProcessedItem
return I вместо использования непроверенного приведения, в этот момент не будет выброшено исключение. Поэтому я также не уверен, что было бы лучше сделать проверку типа во время выполнения, используя Class<I>
объект в getProcessedItem
, Class<I>
объект должен явно обслуживаться вызывающей стороной как параметр или через заданный контейнер. Я чувствую, что процессор не должен заботиться о том, чтобы элементы в контейнере действительно были совместимы с I
потому что процессор не несет ответственности за достоверность содержимого данного контейнера.
2 ответа
Class
класс имеет cast()
метод, который не генерирует никаких предупреждений...
Однако ваша модель неверна. Вы должны спрашивать контейнер для элемента, а не произвольный обработчик элемента. Вы нарушаете инкапсуляцию. Весь ItemProcessor пахнет этим на самом деле. Вы должны рассмотреть полиморфное решение, в котором объекты воздействуют на себя, а не модель, в которой внешний субъект воздействует на объекты извне.
Я думаю, что ваши SuppressWarnings побеждает цель дженериков. Что вам нужно сделать, так это сделать элемент getProcessed общим.
public class ItemProcessor<I extends Processable> {
public I getProcessedItem() {
return somethingWhichIsProcessable;
}