如何在 Java 中初始化一个类,而我要初始化的类会因变量而异?

How do I initialize a class in Java, while the class I want to initialize will vary based on a variable?

提问人:Dominic Miller 提问时间:9/22/2023 更新时间:9/26/2023 访问量:112

问:

我正在尝试制作一个终端程序,该程序将根据用户的输入初始化一个类。有没有办法使用 String 变量初始化特定类,而不会用语句弄乱我的程序?下面是我尝试执行的操作的示例:if

终端:

Which program would you like to run? Please type in lower case.
 - Room designer
 - Etc.

>

法典:

String answer = input.nextLine();
[answer] programToRun = new [answer]();

有没有办法做到这一点?我知道 Python 中有一种方法使用字典,所以我相信有一种方法,只是我无法找到它。Globals

java 对象 oop

评论

1赞 Basil Bourque 9/22/2023
目前尚不清楚。可以解释更多吗?
0赞 Chaosfire 9/22/2023
@BasilBourque 他可能想要类似 if answer = a, then instantiate , if answer = b, then 等等。new A()new B()
0赞 Chaosfire 9/22/2023
@Dominic 这些类是否有一些共同点,例如实现相同的接口或扩展相同的类?
0赞 Dominic Miller 9/22/2023
@Chaosfire他们都要管理文件,只是文件的格式会有所不同。

答:

2赞 ControlAltDel 9/22/2023 #1

执行此操作的方法是使用 Factory 模式

interface Factory<T> {
  T createInstance(String param);
}

public class FactoryManager {
  private Map<String,Factory> factories;
  public void registerFactory(String type, Factory f) ... // add the key/pair to the factories map
  public Object createInstance(String s) ... // match the string to a Factory in the factories Map and call createInstance on it
}

如果您对此有任何疑问,请告诉我

--编辑--

例:

public class Car {
  private make = null;
  public Car() {};
  public Car(String make) {
    this.Car();
    this.make = make;
  }
}

public class CarFactory implements Factory<Car> {
  public Car createInstance(String s) {
    return new Car(s);
  }
}

public class Bus {
  private make = null;
  public Bus() {};
  public Bus(String make) {
    this.Bus();
    this.make = make;
  }
}

public class BusFactory implements Factory<Bus> {
  public Car createInstance(String s) {
    return new Bus(s);
  }
}

public class MyFactoryManager extends FactoryManager {
  public MyFactoryManager() {
//All this actually should be in the base class FactoryManager so it doesn't need to be implemented in each FactoryManager.
    factories = new HashMap<String, Factory>(); 
    public Object createInstance(String s) { // This could be expanded with another parameter to be passed to the factory for object initialization.
      return factories.get(s).createInstance();
    }
  }
}

FactoryManager fm = new MyFactoryManager();
fm.reqisterFactory("car", new CarFactory());
fm.reqisterFactory("bar", new BusFactory());
Object car = fm.createInstance("car");
Object bus = fm.createInstance("bus");
System.out.println("Car is a " + car.getClass());
System.out.println("Bus is a " + bus.getClass());

评论

0赞 Dominic Miller 9/22/2023
我不熟悉工厂模式。不幸的是,我不是最流利的 Java。您能提供更多信息吗?
0赞 ControlAltDel 9/22/2023
@DominicMiller baeldung.com/java-factory-pattern
0赞 Dominic Miller 9/22/2023
是方法吗?在这种情况下,我是否应该扩展它并使用该地图将其全部连接在一起?我有点了解,但我不确定如何整合它。@ControlAltDelregisterFactory
0赞 ControlAltDel 9/22/2023
@DominicMiller是的,registerFactory 是一个方法。我不确定你所说的“扩展它”是什么意思,但如果你的意思是填写方法,以便它将类型/工厂对添加到地图中并使用地图来履行,那么是的createInstanceFactoryManager
0赞 Dominic Miller 9/26/2023
对不起,我知道我很久以前就已经选择了这个作为答案,我知道它会起作用,但我就是无法实现它。你认为我可以举一个例子来解释事情吗?感谢您的耐心等待。@ControlAltDel
0赞 Fabian Witte 9/22/2023 #2

您可以使用以下代码从字符串创建类的实例,使用其 no-args 构造函数(如果不存在此类构造函数,则崩溃):

package plugins;

public class ClassName {
    @Override
    public String toString() {
        return "Works as expected!";
    }
}


public class ClassByNameTest {
    @Test
    void createInstance() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        String className = "plugins.ClassName";
        Object instance = Class.forName(className).getConstructor().newInstance();

        assert a.getClass().getName().equals(className);
        System.out.println(instance.toString());
    }
}

遵循某种约定,例如实现一个公共接口,你可以调用一个入口点方法来动态运行任意行为。

编辑: 尽管这在技术上没有问题,但使用工厂模式的 ControlAltDels 想法是解决这个问题的更好方法,特别是因为它将用户输入与实际类名分离。

0赞 Reilas 9/22/2023 #3

"...有没有办法使用 String 变量初始化特定类,而不会用 if 语句弄乱我的程序?..."

您可以使用反射框架通过 String 值实例化类。

需要提供完全限定的名称。

Class<?> c = Class.forName("java.lang.String");
Object instance = c.getDeclaredConstructor().newInstance();