给定Java中的键字符串如何创建特定类的对象?

时间:2012-11-29 22:12:54

标签: java oop

我正在研究一个动物园的例子,它有几种不同类型的动物。用户输入“添加老虎”等命令,然后将Tiger添加到动物园中。

在我的命令解释器类中,我有一些这样的代码:

String animalName...
Animal newAnimal;
if (animalName.equals("tiger"))
    newAnimal = new Tiger();
else if (animalName.equals("elephant"))
    newAnimal = new Elephant();

这里的问题是当一种新的动物被添加到程序中时,这段代码也必须改变。我想通过继承Animal来添加新动物而不改变现有类中的任何内容。

用户在其命令中给出的名称不一定与动物的类名相同(例如,“添加孟加拉虎”会添加BengalTiger对象)。

如果可能的话,我宁愿避免使用反射。


这是最终代码:

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");
    }
}

5 个答案:

答案 0 :(得分:3)

您应该使用动态类加载: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);

答案 1 :(得分:2)

您需要使用Class.forName(X)才能实现此目的。 Class.forName将帮助您将实际类加载为字符串值。请参阅http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#forName(java.lang.String)。

答案 2 :(得分:1)

Object animalObj = Class.forName(animalString);

但它必须是确切的名称(具有相同的大写字母)。

答案 3 :(得分:1)

为了完整起见,这里是基于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());
    }
}

答案 4 :(得分:0)

反思不是像@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");

使用此解决方案,您只需要使用您需要的动物类型填写该地图! 另一个解决方案,它也是一个干净/良好的解决方案,但非常冗长......