繁体   English   中英

如何在Java中给定键String的情况下创建特定类的对象?

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM