如何在 Java 中使用泛型实现工厂模式?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/34291714/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to implement factory pattern with generics in Java?
提问by user1522820
I have a generic interface Handler
我有一个通用接口处理程序
public interface Handler<T> {
void handle(T obj);
}
I can have n implementations of this interface. Let's say I have following 2 implementations for now. One which handles String objects and another handles Date
我可以有这个接口的 n 个实现。假设我现在有以下 2 个实现。一个处理 String 对象,另一个处理 Date
public class StringHandler implements Handler<String> {
@Override
public void handle(String str) {
System.out.println(str);
}
}
public class DateHandler implements Handler<Date> {
@Override
public void handle(Date date) {
System.out.println(date);
}
}
I want to write a factory which will return handler instances based on the class type. Something like this :
我想编写一个工厂,它将根据类类型返回处理程序实例。像这样的事情:
class HandlerFactory {
public <T> Handler<T> getHandler(Class<T> clazz) {
if (clazz == String.class) return new StringHandler();
if (clazz == Date.class) return new DateHandler();
}
}
I get following error in this factory :
我在这家工厂收到以下错误:
Type mismatch: cannot convert from
StringHandler
toHandler<T>
类型不匹配:无法转换
StringHandler
为Handler<T>
How to fix this?
如何解决这个问题?
采纳答案by Héctor
SIMPLE SOLUTION
简单的解决方案
You could save your mappings Class<T> -> Handler<T>
in a Map
. Something like:
您可以将映射保存Class<T> -> Handler<T>
在Map
. 就像是:
Map<Class<T>, Handler<T>> registry = new HashMap<>();
public void registerHandler(Class<T> dataType, Class<? extends Handler> handlerType) {
registry.put(dataType, handlerType);
}
public <T> Handler<T> getHandler(Class<T> clazz) {
return registry.get(clazz).newInstance();
}
In some place, initialize handlers (could be in the factory itself):
在某些地方,初始化处理程序(可能在工厂本身中):
factory.registerHandler(String.class, StringHandler.class);
factory.registerHandler(Date.class, DateHandler.class);
And in another place, you create and use them:
在另一个地方,您创建和使用它们:
Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
MORE COMPLEX SOLUTION
更复杂的解决方案
You can "scan" classes using reflection and, instead of register manually the mappings Class<T> -> Handler<T>
, do it using reflection.
您可以使用反射“扫描”类,而不是手动注册映射Class<T> -> Handler<T>
,而是使用反射来完成。
for (Class<? extends Handler> handlerType : getHandlerClasses()) {
Type[] implementedInterfaces = handlerType.getGenericInterfaces();
ParameterizedType eventHandlerInterface = (ParameterizedType) implementedInterfaces[0];
Type[] types = eventHandlerInterface.getActualTypeArguments();
Class dataType = (Class) types[0]; // <--String or Date, in your case
factory.registerHandler(dataType, handlerType);
}
Then, you create and use them like above:
然后,您可以像上面一样创建和使用它们:
Handler<String> stringhandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
To implement getHandlerClasses()
, look at thisto scan all classes in your jar
. For each class, you have to check if it is a Handler
:
要实现getHandlerClasses()
,请查看此内容以扫描jar
. 对于每个类,您必须检查它是否为Handler
:
if (Handler.class.isAssignableFrom(scanningClazz) //implements Handler
&& scanningClazz.getName() != Handler.class.getName()) //it is not Handler.class itself
{
//is a handler!
}
Hope it helps!
希望能帮助到你!
回答by OldCurmudgeon
Your problem is that the compiler cannot make the leap to the fact thet the type of the result iscorrect.
你的问题是,编译器不能跃升到一个事实泰德结果的类型是正确的。
To help the compiler you can make the factory delegate the construction. Although this looks strange and unwieldly it does manage to properly maintain type safety without sacrifices such as casting or using ?
or raw types.
为了帮助编译器,您可以让工厂委托构造。尽管这看起来很奇怪且笨拙,但它确实设法正确地维护了类型安全,而无需牺牲诸如强制转换或使用?
或原始类型之类的牺牲。
public interface Handler<T> {
void handle(T obj);
}
public static class StringHandler implements Handler<String> {
@Override
public void handle(String str) {
System.out.println(str);
}
}
public static class DateHandler implements Handler<Date> {
@Override
public void handle(Date date) {
System.out.println(date);
}
}
static class HandlerFactory {
enum ValidHandler {
String {
@Override
Handler<String> make() {
return new StringHandler();
}
},
Date {
@Override
Handler<Date> make() {
return new DateHandler();
}
};
abstract <T> Handler<T> make();
}
public <T> Handler<T> getHandler(Class<T> clazz) {
if (clazz == String.class) {
return ValidHandler.String.make();
}
if (clazz == Date.class) {
return ValidHandler.Date.make();
}
return null;
}
}
public void test() {
HandlerFactory factory = new HandlerFactory();
Handler<String> stringHandler = factory.getHandler(String.class);
Handler<Date> dateHandler = factory.getHandler(Date.class);
}
回答by Albert Bos
You can use something like:
你可以使用类似的东西:
class HandlerFactory {
public <T> Handler<T> getHandler(Class<T> clazz) {
if (clazz.equals(String.class)) return (Handler<T>) new StringHandler();
if (clazz.equals(Date.class)) return (Handler<T>) new DateHandler();
return null;
}
}
T
is generic and the compiler can't map that at compile time. Also it is safer to use .equals
instead of ==
.
T
是通用的,编译器无法在编译时映射它。此外,使用.equals
代替==
.
回答by user690421
The whole point of using a generic type is to share the implementation. If the n implementation of your Handler interface are so different that they can't be shared, then I don't think there is any reason to use define that generic interface at the first place. You'd rather just have StringHandler and DateHandler as top level classes.
使用泛型类型的全部意义在于共享实现。如果您的 Handler 接口的 n 实现是如此不同以至于它们无法共享,那么我认为没有任何理由首先使用定义该通用接口。您宁愿将 StringHandler 和 DateHandler 作为顶级类。
On the other hand, if the implementation can be shared, as is the case of your example, then the factory works naturally:
另一方面,如果实现可以共享,就像你的例子一样,那么工厂自然地工作:
public class Main {
static public interface Handler<T> {
void handle(T obj);
}
static public class PrintHandler<T> implements Handler<T> {
@Override
public void handle(T obj) {
System.out.println(obj);
}
}
static class HandlerFactory {
public static <T> Handler<T> getHandler() {
return new PrintHandler<T>();
}
}
public static void main(String[] args) {
Handler<String> stringHandler = HandlerFactory.getHandler();
Handler<Date> dateHandler = HandlerFactory.getHandler();
stringHandler.handle("TEST");
dateHandler.handle(new Date());
}
}
回答by Mark Tucker
I edited your code and allowed Eclipse to "fix" the errors and it came up with this.
我编辑了您的代码并允许 Eclipse“修复”错误,它想出了这个。
public Handler<?> getHandler(Class<?> clazz) {
if (clazz == String.class)
return new StringHandler();
if (clazz == Date.class)
return new DateHandler();
return null;
}
回答by kunsingh
Yout HandlerFactory don't know about T. Use your factory like below-
你 HandlerFactory 不知道 T. 像下面这样使用你的工厂 -
public class HandlerFactory {
public Handler<?> getHandler(Class<?> clazz) {
if (clazz == String.class) {
return new StringHandler();
}
if (clazz == Date.class) {
return new DateHandler();
}
return null;
}
}
回答by Sercan Ozdemir
Basically you can do:
基本上你可以这样做:
public Handler getHandler( Class clazz ){
if( clazz == String.class ) return new StringHandler();
if( clazz == Date.class ) return new DateHandler();
return null;
}
public static void main( String[] args ){
HandlerFactory handlerFactory = new HandlerFactory();
StringHandler handler = ( StringHandler )handlerFactory.getHandler( String.class );
handler.handle( "TEST" );
DateHandler handler2 = ( DateHandler )handlerFactory.getHandler( Date.class );
handler2.handle( new Date() );
}
Output:
输出:
TEST
Tue Dec 15 15:31:00 CET 2015
But instead writing two different methods to get handlers separately always is a better way.
但是,编写两种不同的方法来分别获取处理程序总是更好的方法。