【问题标题】:How to simplify huge switch-case expression?如何简化巨大的 switch-case 表达式?
【发布时间】:2020-08-27 21:52:10
【问题描述】:

我在使用下一个代码时遇到了一些问题。 我有简单的界面,例如:

public interface Game {
    int start();
}

许多实现此接口的类如:

public class FirstGame implements Game {
    public static final int ID = 1;
    
    @Override
    int start() {
        // Do something and return result
    }
}

而 GameManager 类具有这样的一个方法:

public Game getGameById(int id) {
    switch(id) {
        case FirstGame.ID:
            return new FirstGame();
        case SecondGame.ID:
            return new SecondGame();
        // ..... and many other cases....
    }
    return null;
}

我试图使用反射来简化这个 switch-case 构造,如下所示: 注释:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface GameId {

long value() default 0;
}

FirstGame 类:

@GameId(value = 1)
public class FirstGame implements Game {
    public static final int ID = 1;
    
    @Override
    int start() {
        // Do something and return result
    }
}

GameManager 方法是这样的:

public Game getGameById(int id) {
    Game game = null;
    try {
        Reflections reflections = new Reflections();
        for (Class<?> clazz : reflections.getTypesAnnotatedWith(GameId.class)) {
            if (clazz.getAnnotation(GameId.class).value() == id) {
                Constructor constructor = clazz.getConstructor();
                game = (Game) constructor.newInstance();
                break;
            }
        }
    } catch (Exception ex) { ex.printStackTrace();}
    return game;
}

但它运行得太慢了。 那么,如何以其他方式简化 switch-case 表达式呢? 感谢和抱歉英语不好。

【问题讨论】:

  • 只执行一次并填充地图。考虑ServiceLoader的机制
  • 如果要在case标签中描述字段ID,必须是final
  • saka1029,你的权利。它出现在真实代码中,但不是问题示例。固定

标签: java performance optimization reflection


【解决方案1】:

这个怎么样?

static final List<Supplier<Game>> GAMES = List.of(
    FirstGame::new,
    SecondGame::new
    // ...
);

public Game getGameById(int id) {
    return GAMES.get(id).get();
}

或者

static final Map<Integer, Supplier<Game>> GAMES = Map.of(
    1, FirstGame::new,
    2, SecondGame::new
);

public Game getGameById(int id) {
    return GAMES.get(id).get();
}

【讨论】:

  • 感谢您的回答,但我想防止在创建新游戏类后将新游戏类名称添加到列表中。长长的列表看起来不太好。
【解决方案2】:

感谢 Holger 的回答。 我的反思方法非常接近成功。它只需要将所有构造函数添加到 HashMap 一次,其中 ID 用作键。所以现在我的 GameManager 代码如下所示:

Map<Integer, Constructor> constructorMap = new HashMap<>();

private void fillConstructorMap() {
    try {
        ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
        configurationBuilder.addUrls(ClasspathHelper.forPackage("com.my.package.name"));
        Reflections reflections = new Reflections(configurationBuilder);
        for (Class<?> clazz : reflections.getTypesAnnotatedWith(GameId.class)) {
            Integer id = clazz.getAnnotation(GameId.class).value();
            Constructor constructor = clazz.getConstructor();
            constructorMap.put(id, constructor);
        }
    } catch (NoSuchMethodException ex) {
        ex.printStackTrace();
    }
}

public Game getGameById(int id) {
    Game game = null;
    try {
        game = (Game) constructorMap.get(id).newInstance();
    } catch (Exception ex) {ex.printStackTrace();}
    return game;
}

此方法的运行速度与使用 switch-case 表达式的方法一样快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-14
    • 2011-03-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多