if-else 和 switch 语句的替代方案

Posted

技术标签:

【中文标题】if-else 和 switch 语句的替代方案【英文标题】:Alternative of if-else and switch statements 【发布时间】:2014-05-22 02:23:21 【问题描述】:

我在 Java 中有以下代码:

public void doSomething(int i) 
    if (i == 12) 
        // order should be same
        up();
        left();
        stop();
    
    if (i == 304) 
        // order should be same
        right();
        up();
        stop();
    
    if (i == 962) 
        // order should be same
        down();
        left();
        up();
        stop();
    

// similar code can be done using switch case statements.
// all the function can have any functionality and might not resemble to the name given to them.

现在,如果我被要求不要使用 if-else 和 switch case 语句,那该怎么办?代码可以用 Java 或 javascript 编写。

【问题讨论】:

只是确定一下,您实际上可以使用 JS (this language)?如果是这样,那很容易。 用类似'12'的方法创建一个对象,并在该方法中调用你的函数。 为每个键使用字典,您将拥有函数列表作为值,并根据键选择列表中的函数并迭代列表并调用每个函数。 我永远不会理解要求学生“做 X,但不使用 Y,显然是正确的方法,或 Z 是另一种有效但尴尬的解决方案”的教学原因 “在 Java 或 JavaScript 中”没有意义。它们是两种非常不同的语言,具有两个非常不同的域。为了回答您的问题,两种语言都提供了几种多路 if:if-then-elseif-else 和某种switch。您当前的实现是有缺陷的,因为它命中了 each if 测试,而不是在第一场比赛后退出。这可能就是教练的意思。 【参考方案1】:

如果你会使用 JavaScript,你可以使用带有函数的对象:

function doSomething(i) 
  var obj = ;

  obj[12] = function () 
    // order should be same
    up();
    left();
    stop();
  ;
  obj[304] = function () 
    // order should be same
    right();
    up();
    stop();
  ;
  obj[962] = function () 
    // order should be same
    down();
    left();
    up();
    stop();
  ;

  // apparently we can't use any conditional statements
  try 
    obj[i]();
   catch (e) 

如果只允许使用 ifswitch 语句,请将所有 if 语句替换为逻辑 AND 运算符 (&&):

function doSomething(i) 
  (i == 12) && (
    // order should be same
    up(),
    left(),
    stop()
  );

  (i == 304) && (
    // order should be same
    right(),
    up(),
    stop()
  );

  (i == 962) && (
    // order should be same
    down(),
    left(),
    up(),
    stop()
  );

【讨论】:

第二个是使用 if 语句,但第一个值得投票 ;-) 如果 OP 甚至禁止使用条件语句,它将是不可接受的 :) 只想指出,第二个建议仅适用于使用短路评估的语言:en.wikipedia.org/wiki/Short-circuit_evaluation 从我的头顶开始只有帕斯卡不使用它。 @Hoffmann,但是 OP 正在谈论 Java 和 JS,两者都短路了。 代替try/catch,我个人认为这样更具可读性:obj[i] && obj[i]();【参考方案2】:

您可以制作一个输入到操作的字典。在 Java 中,这将是一个Map<Integer, Runnable>,例如*:

map.put(12, () -> 
    up();
    left();
    stop();
);

然后,您可以获取相应的Runnable 并运行它:

Runnable action = map.get(i);
if (action != null) 
    action.run();
 else 
    // some default action
    // This is the "default" case in a switch, or the "else" in an if-else

if-else 不是绝对必要的,但如果没有它,如果 i 不是预期值,您将得到 NullPointerException — 即您放入地图的值之一。

这个想法在 JavaScript 中是相似的,但使用对象而不是 Maps,函数(可能是匿名的)而不是 Runnables,等等。


此代码适用于 Java 8。在 Java 7 及更低版本中,您可以:

map.put(12, new Runnable() 
    @Override
    public void run() 
        up();
        left();
        stop();
    
);

【讨论】:

@Puru-- 这个想法同样适用于 Java 7。但是,是的,您需要创建一个匿名类而不是使用 lambda。 @sura 是的,但如果您确信 i 始终是预期值,则不需要这样做。 (如果你错了,你会在action.run() 得到一个NullPointerException。) @yshavit,我想你可以try...catch NPE 而不是用if...else 创建一个空保护。 :) @BrianS 你可以,但这通常是个坏主意。如果 Runnable 本身有一个错误并抛出一个 NPE,你永远不会知道,因为那个 catch-and-ignore。最好让它传播出去,这样一些东西就可以对你大喊大叫。 谁说你必须忽略异常? (并不是我认为 try/catch 是 if/else 的 good 替代品,只是在这种情况下它可以,并且符合 OP 的要求。)【参考方案3】:

看,自然你应该用条件表达式检查条件语句! 现在,如果你不想这样做,你可以像这样不自然地这样做:

首先对所有方法(向上、向下、...)执行此操作

java.lang.reflect.Method method;
try 
  method = obj.getClass().getMethod(methodName, param1.class, param2.class, ..);
 catch (SecurityException e) 
  // ...
 catch (NoSuchMethodException e) 
  // ...

不要使用整数传递给doSomething,而是使用包含要调用的方法名称的数组,并在 for 循环中以这种方式调用每个方法:

然后你通过调用来调用该方法

try 
  method.invoke(obj, arg1, arg2,...);
 catch (IllegalArgumentException e) 
 catch (IllegalAccessException e) 
 catch (InvocationTargetException e) 

很遗憾,Java 没有委托

【讨论】:

虽然我相信这种方法会工作(因为它会产生预期的结果),但反射是解决这个问题的一个不必要的复杂解决方案,IMO。 @BrianS- 实际上,正如我已经说过的,在 java 中最自然的方法是使用 if 语句,但是由于 OP 不想要它,我尝试尽可能接近 c#代表,这是java中缺少的。 常见的 Java 解决方案是观察者模式的一些变体(yshavit 的回答是简化版本)。不过,我承认,几年前我 wrote a class 以可重用的方式实现您的解决方案。【参考方案4】:

对于 js 你可以试试这个:

// define ur actions here
var actions = 
  "12" : function ()  up(); left(); stop(); ,
  "304" : function ()  right(); up(); stop(); ,
  "962" : function ()  down(); left(); up(); stop(); 
;

function doSomething(i) 
  var fn = actions[i];
  try 
    fn();
   catch (err) 
    console.error(err);
  

//
doSomething(12); //invoke here

【讨论】:

写得非常巧妙。【参考方案5】:

这是在 JavaScript 中完成此操作的简单方法:

function up()     console.log("up");    
function down()   console.log("down");  
function left()   console.log("left");  
function right()  console.log("right"); 
function stop()   console.log("stop");  

var fnmaps = 
    12:  [up, left, stop],
    304: [right, up, stop],
    962: [down, left, up, stop]
;

function doSomething(i) 
    var fnmap = fnmaps[i] || [], j;
    for (j = 0; j < fnmap.length; j++) 
        fnmap[j]();
    


doSomething(12);
doSomething(304);
doSomething(962);

只需编辑地图变量即可添加/排序功能。

【讨论】:

【参考方案6】:

那就用while和break吧,没有条件检查就没有办法了

public void doSomething(int i) 

while(i == 12) 
// order should be same
    up();
    left();
    stop();
break;
    

while(i == 304) 
// order should be same
    right();
    up();
    stop();
break;
    

while(i == 962) 
// order should be same
    down();
    left();
    up();
    stop();
break;
    

【讨论】:

替代,同样糟糕:(i == 12 && (up (), left (), stop ())); (i == 304 && (右(), 上(), 停止())); (i == 962 && (下(), 左(), 上(), 停()));【参考方案7】:

在 Java 中,这应该可以工作..

public class IfTest 

    public static void main(String[] args) 
        IfTest ifTest = new IfTest();

        ifTest.doSomething(12);
        ifTest.doSomeThingWithoutIf(12);

        ifTest.doSomething(304);
        ifTest.doSomeThingWithoutIf(304);

        ifTest.doSomething(962);
        ifTest.doSomeThingWithoutIf(962);

        ifTest.doSomething(42);
        ifTest.doSomeThingWithoutIf(42);
    

    public void doSomeThingWithoutIf(int i) 


        boolean x = i == 12 ? f12() : (i == 304 ? f304() : (i == 962 ? f962() : false));
    

    public boolean f12() 

        up();
        left();
        stop();

        return true;
    

    public boolean f304() 

        right();
        up();
        stop();
        return true;
    

    public boolean f962() 

        down();
        left();
        up();
        stop();

        return true;
    

    public void doSomething(int i) 

        if(i == 12) 
            // order should be same
            up();
            left();
            stop();
        

        if(i == 304) 
            // order should be same
            right();
            up();
            stop();
        

        if(i == 962) 
            // order should be same
            down();
            left();
            up();
            stop();
        
    

    private boolean retfalse() 
        return false;
    

    private void down() 
        System.out.println("down");
    

    private void right() 
        System.out.println("right");
    

    private void stop() 
        System.out.println("stop");
    

    private void left() 
        System.out.println("left");
    

    private void up() 
        System.out.println("up");
    

【讨论】:

我会假设它是因为你用三元运算符做了 if-else。我认为 OP 想要一个没有 if-else 或同义词的解决方案。 如果是这样的话,他肯定说过“不使用条件运算符”左右。我不擅长 JS,但是如果我查看为 Java 发布的解决方案,它们要么使用反射,要么使用 Map/Dictionary 等。对于这么简单的事情,我发现两者的性能和内存占用都很糟糕 我不一定不同意你zencv。我只是提供了一个可能的原因来说明为什么它被否决了;)【参考方案8】:

这个问题可以使用 OOP 技术来解决。

在java中它看起来像这样:

public abstract class AObject
   public abstract  void doSomething();
   public void  up()
      //do something here
   
   public void down()
      //do something here
   
   public void left()
      //do something here
   
   public void right()
      //do something here
          
   public void stop()
      //do something here
        



public class AObject12 extends AObject
   public void doSomething()
      up();
      left();
      stop();
   


public class AObject304 extends AObject
   public void doSomething()
      right();
      up();
      stop();
   


public class AObject962 extends AObject
   public void doSomething()
      down();
      left();
      up();
      stop();
   

在具体类的实例上执行 doSomething 将触发适当的行为。所以不再需要 if/else。示例见以下代码:

AObject A12 = new AObject12();
A12.doSomething();
// Will run Up left stop in that order

AObject A304 = new AObject304();
A304.doSomething();
// Will run right Up stop in that order

AObject A962 = new AObject962();
A962.doSomething();
// Will run down left up stop in that order

更多关于这种编程的信息可以在这里找到:http://en.wikipedia.org/wiki/Polymorphism_%28computer_science%29

如果您希望能够动态更改对象的行为,可以考虑应用状态模式:http://en.wikipedia.org/wiki/State_pattern

【讨论】:

你现在如何有条件地调用一个? @Ven 您在对象的实例上调用 do instance,它将执行属于该实例的代码。假设您希望这种行为是可设置的。那么你可以考虑使用状态模式:en.wikipedia.org/wiki/State_pattern. 但是你如何实例化正确的类呢?您没有在这里回答主要问题。 这仍然不正确,您正在执行每一个行为,而不是依赖于“i”的值。 i 的值在对象中表示,它不再是 var。但这取决于对象的类型。它仍然包含与 OP 的代码类似的逻辑,但没有 if/else,就像 OP 所要求的那样。 :)【参考方案9】:

Wahahahahaha,你拯救了我的一天 :) 这应该有效,现在无法测试..

public void doSomething(int i) 

try 
    int x = 1/(12-i); // fails for i==12
 catch (ArithmeticException e) 
        up();
        left();
        stop();

等等,尽情享受吧!

【讨论】:

【参考方案10】:

在 JAVA 中可以通过使用逻辑 AND 运算符的短路来实现所需的行为,如下所示:

public static Boolean seq12() 
    // order should be same
    up();
    left();
    stop();
    return true;

public static Boolean seq304() 
    // order should be same
    right();
    up();
    stop();
    return true;

public static Boolean seq962() 
    // order should be same
    down();
    left();
    up();
    stop();
    return true;


public static void doSomething(int i) 
    Boolean tmp;
    tmp = (i == 12 && seq12());
    tmp = (i == 304 && seq304());
    tmp = (i == 962 && seq962());

这段代码非常简单,并且依赖于逻辑与运算符仅在第一个操作数为 true 时计算第二个操作数的事实。

【讨论】:

以上是关于if-else 和 switch 语句的替代方案的主要内容,如果未能解决你的问题,请参考以下文章

用if-else语句替换程序中switch语句

switch语句和switch-case与if-else之间的转换

有啥完美的方法替代java中的 if-else,switch-case

Golang教程:switch 语句

Scala 基础知识

有啥完美的方法替代java中的 if-else,switch-case