在创建对象时使用带有"new"的变量

lae*_*des 13 java

我正在设计一个虚拟水族馆.我有一个类:我继承的鱼,以创建不同物种的类.用户可以在组合框中选择物种,然后单击按钮将鱼放入罐中.我使用以下代码创建鱼:

    switch(s){
        case "Keegan" :
            stock.add(new Keegan(this, x,y));
            break;
        case "GoldenBarb" :
            stock.add(new GoldenBarb(this, x,y));
Run Code Online (Sandbox Code Playgroud)

"stock"是LinkedList,"s"是在Jcombobox中选择的String.当我添加一堆不同的物种时,我将不得不创建一个长开关.我希望代码看起来像:

stock.add(new s(this,x,y));
Run Code Online (Sandbox Code Playgroud)

并且省去了开关,这样我所要做的就是创建类并将其名称添加到组合框并让它工作.有办法吗?任何帮助表示赞赏.

mil*_*ose 8

您想要使用一堆工厂对象,存储在Map您在其中使用的字符串键下switch.

这些是您应该拥有的各种鱼类.

abstract class FishBase {}

class Keegan extends FishBase {
    Keegan(Object _this, int x, int y) {
        // ...
    }
}
class GoldenBarb extends FishBase {
    GoldenBarb(Object _this, int x, int y) {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

所有鱼类工厂的界面.鱼类工厂代表了一种创造某种鱼类的方法.你没有提到构造函数签名是什么,所以我选择了一些类型.

interface IFishFactory {
    FishBase newFish(Object _this, int x, int y);
}
Run Code Online (Sandbox Code Playgroud)

为每种鱼类设置一个工厂.这些显然不需要是匿名类,我用它们来减少混乱.

Map<String, IFishFactory> fishFactories = new HashMap<>();

fishFactories.put("Keegan", new IFishFactory() {
    public FishBase newFish(Object _this, int x, int y) {
        return new Keegan(_this, x, y);
    }
});

fishFactories.put("GoldenBarb", new IFishFactory() {
    public FishBase newFish(Object _this, int x, int y) {
        return new GoldenBarb(_this, x, y);
    }
});
Run Code Online (Sandbox Code Playgroud)

然后从Map使用已有的字符串中选择工厂.您可能想要检查是否存在给定名称的工厂.

stock.add(fishFactories.get(s).newFish(this, x, y));
Run Code Online (Sandbox Code Playgroud)

现在,如果所有的fish类都具有完全相同的构造函数签名,则可以创建一个可以使用反射处理所有类的工厂类,并删除一些样板.

class ReflectionFishFactory implements IFishFactory {
    Constructor<? extends FishBase> fishCtor;
    public ReflectionFishFactory(Class<? extends FishBase> fishClass) 
            throws NoSuchMethodException {

        // Find the constructor with the parameters (Object, int, int)
        fishCtor = fishClass.getConstructor(Object.class, 
                                            Integer.TYPE, 
                                            Integer.TYPE);
    }


    @Override
    public FishBase newFish(Object _this, int x, int y) {
        try {
            return fishCtor.newInstance(_this, x, y);
        } catch (InstantiationException
                | InvocationTargetException
                | IllegalAccessException e) {
            // this is terrible error handling
            throw new RuntimeException(e);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后为每个适用的子类注册它.

for (Class<? extends FishBase> fishClass : 
        Arrays.asList(Keegan.class,GoldenBarb.class)) {
    fishFactories.put(fishClass.getSimpleName(), 
                      new ReflectionFishFactory(fishClass));
}
Run Code Online (Sandbox Code Playgroud)


lin*_*fox 7

我认为反思可能就是你在寻找的东西.这允许您避免使用switch语句,这就是您所要求的.

反射(除其他外)允许您只使用字符串运行方法.所以在Java中,你通常会调用这样的方法:

new Foo().hello();
Run Code Online (Sandbox Code Playgroud)

使用Reflection,您可以使用字符串来调用方法,如下所示:

Class<?> clazz = Class.forName("Foo");
clazz.getMethod("hello").invoke(clazz.newInstance());
Run Code Online (Sandbox Code Playgroud)

Java构造函数反射示例.


关于工厂模式(现在参考其他答案),据我所知,这只是封装switch语句(或您选择使用的任何方法).Factory模式本身不是避免switch语句的一种方法.工厂模式是一件好事,但不是你问的问题.(在任何情况下,您可能都希望使用工厂模式).

  • 反思是slooooooowwwww.如果这个应用程序不是很大可能无关紧要,但恕我直言,反思只是一个精心设计的架构的不良替代品.我给予反思的唯一一点就是它非常适合开发插件环境. (5认同)
  • 另外,如果它只是很少执行,那么使用反射的速度有多慢并不重要.并且"一旦响应用户输入事件"计为"很少". (5认同)
  • 反思是fleeeeeexxxxiiiiiibbbbllllleeee.:P动态类选择在没有它的情况下不会像Java这样的静态类型语言那么容易实现. (3认同)
  • 这真的不是这个的正确场所. (3认同)

Adr*_*hum 6

让我们一步一步看看你想走多远.

首先,您可以在FishFactory中抽象出鱼的创建,这样您执行switch语句的原始位置可以简单地更改为

stock.add(fishFactory.createFish(s, x, y));
Run Code Online (Sandbox Code Playgroud)

然后开关盒进入工厂:

public class SimpleFishFactory {
    @Override
    public Fish createFish(String fishType, int x, int y) {
        switch(s){
            case "Keegan" :
                return new Keegan(this, x,y);
                break;
            case "GoldenBarb" :
                return new GoldenBarb(this, x,y);
            //....
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

(我假设你的所有鱼都有与Fish相同的界面/基类)

如果您想让创作看起来更优雅,有两种常用的方法可供选择:

反思 想法很简单.首先设置字符串与鱼类(或构造函数)的查找表,每个createFish()都通过反射创建鱼的新实例

public class ReflectionFishFactory {

    private Map<String, Class<? extends Fish>> fishClasses = new HashMap<...>();

    public ReflectionFishFactory() {
        //set up fishClasses with name vs corresponding classes.
        // you may read it from file, or hard coded or whatever

        fishClasses.put("Keegan", Keegan.class);
        fishClasses.put("GoldenBarb", GoldenBarb.class);
    }


    @Override
    public Fish createFish(String fishType, int x, int y) {
        Class<?> fishClass = fishClasses.get(fishType);
        // use reflection to create new instance of fish by 
        // by using fishClass
    }
}
Run Code Online (Sandbox Code Playgroud)

原型模式 由于某些原因,您可能不想使用反射(可能由于反射的缓慢,或者不同的鱼有非常不同的创建方式),您可以查看GoF的原型模式.

public class PrototypeFishFactory {

    private Map<String, Fish> fishes = new HashMap<...>();

    public ReflectionFishFactory() {
        //set up fishClasses with name vs corresponding classes.
        // you may read it from file, or hard coded or whatever

        fishClasses.put("Keegan", new Keegan(....) );
        fishClasses.put("GoldenBarb", new GoldenBarb(....) );
    }


    @Override
    public Fish createFish(String fishType, int x, int y) {
        return fishes.get(fishType).cloneNewInstance(x, y);
    }
}
Run Code Online (Sandbox Code Playgroud)