Java параметризованный универсальный статический завод
Возможно ли в Java создать статический фабричный метод / класс, который использует интерфейс в качестве параметризованного типа, и вернуть реализующий класс этого данного интерфейса?
Хотя мои знания в области дженериков ограничены, я хочу сделать следующее:
// define a base interface:
public interface Tool {
// nothing here, just the interface.
}
// define a parser tool:
public interface Parser extends Tool {
public ParseObject parse(InputStream is);
}
// define a converter tool:
public interface Converter extends Tool {
public ConvertObject convert(InputStream is, OutputStream os);
}
// define a factory class
public class ToolFactory {
public static <? extends Tool> getInstance(<? extends Tool> tool) {
// what I want this method to return is:
// - ParserImpl class, or
// - ConverterImpl class
// according to the specified interface.
if (tool instanceof Parser) {
return new ParserImpl();
}
if (tool instanceof Converter) {
return new ConverterImpl();
}
}
}
Я хочу ограничить код клиента, чтобы он вставлял только интерфейсный тип в метод getInstance(), который расширяется из интерфейса Tool, который я указал. Таким образом, я точно знаю, что вставленный тип инструмента является законным инструментом.
Код клиента должен выглядеть так:
public class App {
public void main(String[] args) {
Parser parser = null;
Converter converter = null;
// ask for a parser implementation (without knowing the implementing class)
parser = ToolFactory.getInstance(parser);
// ask for a converter implementation
converter = ToolFactory.getInstance(converter);
parser.parse(...);
converter.convert(... , ...);
}
}
Фабрика должна включить тип интерфейса (небрежно, если он нулевой или нет), определенный до того, как его попросят с фабрики. Я знаю, что это не сработает так, как я это написал, но я надеюсь, что один из читателей знает, чего я хочу достичь.
Тип возвращаемого значения метода getInstance совпадает с входящим параметром, поэтому при передаче интерфейса Parser он также возвращает Parser p = new ParserImpl(); возврат р;
Заранее спасибо за помощь.
1 ответ
Пара вещей:
- Ваша фабрика почти наверняка должна использовать экземпляр класса, а не объект Tool. Когда кто-то создает
Parser
перейти в ваш метод, чтобы получитьParser
немного курицы с яйцом. - Я не знаю, разрешено ли вам иметь общие параметры для методов, которые являются подстановочными знаками; Я полагаю, нет, так как это было бы бессмысленным и бессмысленным. Когда вы параметризируете метод, вам нужно дать универсальному параметру имя, чтобы вы могли обратиться к нему позже.
Собрав их вместе, ваш фабричный метод может выглядеть примерно так:
public static <T extends Tool> T getInstance(Class<T> toolClass) {
if (Parser.class.isAssignableFrom(toolClass) {
return new ParserImpl();
}
else if (Converter.class.isAssignableFrom(toolClass) {
return new ConverterImpl();
}
// You'll always need to have a catch-all case else the compiler will complain
throw new IllegalArgumentException("Unknown class: " + toolClass.getName());
}
Если вы хотите ограничить тип toolClass
чтобы быть интерфейсом, вы не можете сделать это во время компиляции, но вы, конечно, можете ввести проверку во время выполнения toolClass.isInterface()
,
Кстати, это статическое переключение с жестким кодом не очень хорошо в целом. На мой взгляд, было бы лучше поместить отношение класс-конструктор в Map
и посмотреть на процесс строительства динамически. Может быть, даже сохранить значение как Callable<? extends Tool>
и добавьте защищенный метод, позволяющий другим классам регистрировать сопоставления.
Это не значит, что ваша текущая версия не работает, просто она не очень хорошо масштабируется, и сейчас я не думаю, что она делает много для оправдания наличия отдельной фабрики, а не просто вызова вызывающей стороны toolClass.newInstance()
самих себя.