【问题标题】:Switch case on object with two instance variables使用两个实例变量在对象上切换大小写
【发布时间】:2020-01-30 21:53:05
【问题描述】:

在 Java 中是否可以使用对象而不是原始类型来使用 switch-case 语句?

我有一个场景,我有很多 2d 位置 (x,y),我希望它们每个在被触发后表现不同。

例如,我想:

Pos pos = getNextPos();

switch (pos) {
    case new Pos(1, 2):
        // do something
        break;
    case new Pos(9, 7):
        // do something else...
        break;
    etc...
}

或许

Pos pos = getNextPos();
Pos[] listOfPos = getListOfPos();

switch (pos) {
    case listOfPos[0]:
        // do something
        break;
    case listOfPos[1]:
        // do something else...
        break;
    etc...
}

我还在 Pos 类中实现了 .equals() 方法,仅当 x 和 y 都等于另一个对象时才返回 true。

Pos 类(带有自动生成的 equals 和 hashCode):

public class Pos {
    public int x;
    public int y;

    public Pos(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Pos pos = (Pos) o;
        return x == pos.x && y == pos.y;
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

我已经尝试了所有这些,但是在编译时我得到“不兼容的类型:Pos 无法转换为 int”。

【问题讨论】:

  • 你不能那样做。来自tutorial page:“开关适用于 byte、short、char 和 int 原始数据类型。它也适用于枚举类型(在 Enum Types 中讨论)、String 类和一些包装某些原始数据的特殊类类型:字符、字节、短整数和整数(在数字和字符串中讨论)。”有关 Java 中开关的更多讨论,请参阅 this question

标签: java


【解决方案1】:

使用 Java 8 和 lambda,您可以不用 switch

class Foobar {
  private final Map<Pos, Consumer<Pos>> mapping;

  Foobar() {
    mapping = new HashMap<>();
    mapping.put(new Pos(1, 2), this::doSomething1);
    mapping.put(new Pos(5, 2), this::doSomething2);
  }

  private void doSomething1(Pos pos) { ... }
  private void doSomething2(Pos pos) { ... }

  public void handleNextPost() {
    Pos pos = getNextPos();
    Consumer<Pos> consumer = mapping.get(getNextPos());
    if (null != consumer) {
      consumer.accept(pos);
    }
  } 
}

顺便说一句,在字节码级别,String 上的 switchHashMap 的数量几乎相同:使用 hashCode()tableswitch使用goto 的下一条语句。

上面的代码很简单:

  • 我声明了一个 map,其中包含每个 Pos 您想要执行的操作。
  • 每个动作都由采用Pos的方法实现
  • 当您阅读getNextPos() 时,您会搜索处理所述位置的Consumer
  • ConsumernextPos 一起调用。

您可以在本地定义它,但您必须确保不会每次都创建 mapping

如果你真的必须坚持使用switch,你不需要任何特别的东西:你将int 用于Pos x/yswitch 自然适用于int

switch (pos.getX()) {
  case 0: switch (pos.getY()) {
    case 1: { // [0, 1]
      break;
    }
    case 3: { // [0, 3]
       break;
    }     
  }
  break;
  // ... and so on
}

这很容易让你的代码更难阅读,所以避免它。

另外,map 不适用于松散匹配(例如:对 x = 1 的每个 Pos 采取的操作),您可能必须使用其他替代方法。

【讨论】:

    【解决方案2】:

    对于第二个例子,

    int pos = Arrays.asList(getNextPos()).indexOf(getNextPos());
    

    将允许老派switch

    这是一个肮脏、肮脏的 hack,但使用 switch-on-string,你可以:

     switch (pos.toString()) {
         case "1,2": ....
    

    这确实需要一个合适且稳定的toString 方法,无论如何这可能不是一个糟糕的主意。

    更可怕的是:

     switch (pos.x+","+pos.y) {
         case "1,2": ....
    

    不要尝试使用用户提供的字符串。

    Oracle 曾抱怨将 instanceof 模式匹配 (JEP 305) 扩展到完整的 switch 解构器,其中可能包括属性值匹配。

    另一种解决方法是将MapPos 用于适当的功能类型。或许else-if 链还不错。

    【讨论】:

    • 模式匹配功能的一部分,记录,在 JDK 14 中作为预览功能出现,但在模式匹配准备好使用之前,它可能需要更多版本。我一直在关注事态的发展,看起来很有趣。这是 Brian Goetz 和 Gavin Bierman 在 2018 年写的关于它的内容:cr.openjdk.java.net/~briangoetz/amber/pattern-match.html
    【解决方案3】:

    您可以使用嵌套的 switch-case,但是当出现数万亿个 case 时,这可能很难阅读。

    Pos pos = new Pos(2,3);
    
    switch(pos.getX()){
        case "2":
            switch(pos.getY()){
                case "3":
                    do something with Pos(2,3);
    

    或者您可以通过 Nirbhay Mishra 的多态性 Alternative to Nested Switch Statements in Java 查看这个答案。

    【讨论】:

    • 我查看了多态性答案并对它感兴趣,但我真的不知道我将如何自己实现它,你知道我如何将它集成到我的示例中吗?
    • 在我看来,在这种情况下,多态性似乎并不容易工作。有很多教程可以用多态性替换 switch-cases,至少在我看来,使用 10^2 种不同的组合会很麻烦。
    【解决方案4】:

    这在某种程度上类似于 switch 语句:

        Pos pos = getNextPos();
    
        Optional.ofNullable(Map.of(new Pos(1, 2), (Runnable) () -> {
    
            System.out.println(1);
    
        }, new Pos(3, 2), (Runnable) () -> {
    
            System.out.println(2);
    
        }).get(pos)).orElse((Runnable) () -> { // Default
    
            System.out.println(3);
    
        }).run();
    

    映射到 Runnables 的 Pos-Objects 映射。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-18
      • 2016-06-09
      • 1970-01-01
      • 2021-11-03
      • 2016-02-27
      • 1970-01-01
      相关资源
      最近更新 更多