我有两个变量foo
和bar
,它们决定了我需要创建哪种对象。目前,这两个都可以包含两个不同的值,并且对象是这样创建的:
Result createResult(int foo, int bar) {
if(foo == 0) {
if(bar == 0) return new FirstResult();
if(bar == 1) return new SecondResult();
}else if(foo == 1) {
if(bar == 0) return new ThirdResult();
if(bar == 1) return new FourthResult();
}
return null;
}
// Common interface for all the objects
interface Result {
}
这项工作只适用于这四种类型,但如果还有更多类型呢?如何处理对象创建以使其更有效?
答案 0 :(得分:3)
if / else模式应该非常高效,直到相当大的尺寸。然而,如果事情变得非常巨大,这种变化和可读性都会受到影响。我建议使用HashMap-Lookup来获得正确的构造函数,以便更快地确定结果。这要求您创建一个可用作HashMap
:
class CreationParams {
private final int foo;
private final int bar;
CreationParams(final int foo, final int bar) {
this.foo = foo;
this.bar = bar;
}
// make sure to implement hashCode & equals so this class can be efficiently used in a Map
}
正如上面评论中已经提到的,您绝对需要实施hashCode
和equals
。如果你没有实现equals
或hashCode
它将无法工作,因为如果密钥不相同(即使相等),hashmap查找将失败。
现在,您可以轻松地将参数组合映射到构造函数并查找结果。考虑这个例子:
class ResultFactory {
private static final Map<CreationParams, Supplier<Result>> factories = createFactoryMap();
private static Map<CreationParams, Supplier<Result>> createFactoryMap() {
final Map<CreationParams, Supplier<Result>> result = new HashMap<>();
result.put(new CreationParams(0, 0), FirstResult::new);
result.put(new CreationParams(0, 1), SecondResult::new);
// ...
return result;
}
Result createResult(int foo, int bar) {
return factories.get(new CreationParams(foo, bar)).get();
}
}
关键是你的所有构造函数(构造函数的代理人)现在都保存在地图factories
中。一旦达到某个关键大小,查找factories.get(new CreationParams(foo, bar))
将比许多if语句更快,因为它不需要迭代所有可能的目标对象,而只需要迭代具有冲突哈希的目标对象。然后你可以立即调用get
或在现实世界中你可能想要检查null并在这种情况下抛出某种异常。
如果您坚持使用较旧的Java版本,则基本上有两种可能的解决方法。在这两种情况下,您都需要像这样创建自己的供应商界面(从技术上讲,您不需要反射变量的界面,因为它只需要一个您也可以直接使用的实现类):
interface Supplier {
Result get();
}
然后一种方法是使用需要较少源代码的反射:
class ReflectionSupplier implements Supplier {
final Class<? extends Result> clazz;
ReflectionSupplier(final Class<? extends Result> clazz) {
this.clazz = clazz;
}
public Result get() {
try {
return clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new Error(e);
}
}
}
现在您可以按如下方式向Map添加类:
result.put(new CreationParams(0, 0), new ReflectionSupplier(FirstResult.class));
另一种选择是为每个实例使用(匿名)类。优点是有一堆错误可以发现编译时间(例如没有默认构造函数可用)。缺点是这会产生大量的线条。只需像这样添加到您的地图:
result.put(new CreationParams(0, 0), new Supplier() {
@Override
public Result get() {
return new FirstResult();
}
});
答案 1 :(得分:1)
好吧,让我们试试小鸡
quotes = open("/Users/AquaAurelius/Documents/Home/Programming/Houston/YOUR_FILENAME.txt")
如果构造函数有参数可能类似..
private String [][] classNames = new String[][]{
{"com.something.FirstResult", "com.something.SecondResult"},
{"com.something.ThirdResult", "com.something.ThirdResult"}
};
Result createResult(int foo, int bar) {
Class<?> clazz = Class.forName(classNames[foo][bar]);
return clazz.newInstance();
}
同样是洋基建议:
Class<?> clazz = Class.forName("com.something.SomeClass");
Constructor<?> constructor = clazz.getConstructor(String.class, Integer.class);
Object instance = constructor.newInstance("some string", 0);
将是实现类似于我的想法的另一种方式。
答案 2 :(得分:1)
这可能有些过分,但您可以使用数据结构将foo / bar对映射到Class
实例,然后在该实例上调用newInstance()
来创建对象。这里或多或少是使用二维数组存储Class
es的外观:
static Class[][] classes = new Class[][]{
{ FirstResult.class, SecondResult.class},
{ ThirdClass.class,FourthResult.class}
}
Result createResult(int foo, int bar){
if (foo < 0 || foo >= classes.length)
return null;
if (bar < 0 || bar >= classes[foo].length)
Return null;
Try {
return classes[foo][bar].newInstance();
} catch (InstantiationException ie) {
// Shouldn't happen
} catch (IllegalAccessException iae) {
// also shouldn't happen
}
}
这段代码不在我的脑海中,所以我不保证它会按原样运行,但它应该给你一般的想法......