[英]How to create an object of an specific class given a key String in Java?
我正在研究一个具有几种不同类型动物的动物园的示例。 用户输入命令“添加老虎”,然后将老虎添加到动物园。
在我的命令解释器类中,我有一些类似以下的代码:
String animalName...
Animal newAnimal;
if (animalName.equals("tiger"))
newAnimal = new Tiger();
else if (animalName.equals("elephant"))
newAnimal = new Elephant();
这里的问题是,当将一种新的动物添加到程序中时,也必须更改这段代码。 我只想通过子类化Animal来添加新动物,而无需更改现有类中的任何东西。
用户在其命令中给定的名称不一定与动物的类名相同(例如,“添加孟加拉虎”将添加一个孟加拉虎对象)。
如果可能的话,我宁愿避免使用反射。
这是最终代码:
private static String getClassName(String name) {
char ch;
int i;
boolean upper = true;
StringBuilder s = new StringBuilder("animals.");
for (i=0; i<name.length(); i++) {
ch = name.charAt(i);
if (ch!=' ') {
if (upper)
s.append(Character.toUpperCase(ch));
else
s.append(Character.toLowerCase(ch));
upper = false;
} else
upper = true;
}
return s.toString();
}
@Override
public Animal addAnimal(String s) {
try {
Animal animal = (Animal)Class.forName(getClassName(s)).newInstance();
addAnimal(animal);
return animal;
} catch (InstantiationException e) {
throw new IllegalArgumentException("There are no animals called like this");
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("There are no animals called like this");
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("There are no animals called like this");
} catch (ClassCastException e) {
throw new IllegalArgumentException("There are no animals called like this");
}
}
您应该使用动态类加载: Class.forName(name)
,例如:
String animal = ... // e.g. elephant
String className = "com.mycompany.animals." + animal.substring(0, 1).toUpperCase() + animal.subString(1);
Animal obj = (Animal)Class.forName(className);
您需要使用Class.forName(X)来实现。 Class.forName将帮助您获取加载实际类的字符串值。 请参阅http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#forName(java.lang.String )。
Object animalObj = Class.forName(animalString);
但是它必须是确切的名称(大小写相同)。
为了完整起见,这是基于AbstractFactory模式的详细解决方案。 您将需要将两个接口而不是一个接口子类化(动物类型的工厂和动物类型本身),但是,嘿!
interface Animal {
String whoAmI();
}
class Tiger implements Animal {
@Override
public String whoAmI() {
return "Tiger";
}
}
interface AnimalFactory {
Animal create();
}
abstract class AbstractAnimalFactory implements AnimalFactory {
protected AbstractAnimalFactory(String animalName, FactoriesRegistry registry) {
registry.register(animalName, this);
}
}
class TigersFactory extends AbstractAnimalFactory {
TigersFactory(FactoriesRegistry registry) {
super("tiger", registry);
}
@Override
public Animal create() {
return new Tiger();
}
}
class FactoriesRegistry {
HashMap<String, AnimalFactory> byAnimalName = new HashMap<String, AnimalFactory>();
Animal produce(String id) {
AnimalFactory factory = byAnimalName.get(id);
if (factory != null)
return factory.create();
else
throw new IllegalArgumentException("Unknown animal " + id);
}
public void register(String animalName, AnimalFactory factory) {
byAnimalName.put(animalName, factory);
}
}
public class SOSample {
public static void main(String[] args) {
FactoriesRegistry registry = new FactoriesRegistry();
TigersFactory tigersFactory = new TigersFactory(registry);
System.out.println(registry.produce("tiger").whoAmI());
}
}
像@Victor Sorokin一样,反射并不是解决此问题的唯一方法。 但这是最简单/干净的代码!!!
反射解决方案:
public interface Animal {
// put your contract here
}
public class AnimalFactory {
public static final AnimalFactory INSTANCE = new AnimalFactory();
private Map<String, Class<? extends Animal>> animalTypeMap;
private AnimalFactory() {};
public Animal createAnimal(String key) {
Animal animal = null;
if (animalTypeMap != null) {
Class<? extends Animal> animalType = animalTypeMap.get(key);
if (animalType != null) {
try {
animal = animalType.newInstance();
} catch (Throwable error) {
throw new RuntimeException("Unable to create animal with key='" + key + "'", error);
}
}
}
return animal;
}
public Map<String, Class<? extends Animal>> getAnimalTypeMap() {
if (animalTypeMap == null) {
animalTypeMap = new HashMap<String, Class<? extends Animal>>();
}
return animalTypeMap;
}
public void setAnimalTypeMap(Map<String, Class<? extends Animal>> animalTypeMap) {
this.animalTypeMap = animalTypeMap;
}
public void addAnimalType(String key, Class<? extends Animal> animalType) {
getAnimalTypeMap().put(key, animalType);
}
public void removeAnimalType(String key, Class<? extends Animal> animalType) {
if (animalTypeMap != null) {
animalTypeMap.remove(key);
}
}
}
然后,您可以像这样使用它:
Animal myAnimal = AnimalFactory.INSTANCE.createAnimal("myAnimalKey");
使用此解决方案,您只需要在地图中填写所需的动物类型即可! 另一个解决方案也是干净/好的解决方案,但是非常冗长...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.