【问题标题】:How can I replace a switch in Java with a list that references a variable for each case?如何用引用每种情况下的变量的列表替换 Java 中的开关?
【发布时间】:2017-01-12 01:06:59
【问题描述】:

我在这里找不到像这样的先前问题(这让我感到惊讶),所以..

我有一个有效的 Java 程序,其中一部分分析键入的输入。一些输入“选项”是以下情况下的字符串。

switch (varName.toLowerCase())  {
    case "steps":
        common.steps = true;
        break;
    case "scale":
        common.scale = true;
        break;
    case "float":
        common.fracts = false;
        break;
    case "fraction":
        common.fracts = true;
        break;
    case "spaces":
        common.spaces = false;
        break;
    ... etc.
}

在 C 或 C++ 中,我可以通过创建一个列表(它是一个包含字符串名称和变量指针的结构)来缩短这段代码,例如

LIST varAction[] = { { "steps", &common.steps },
                     { "scale", &common.scale },
                     .. etc.
                   };

然后简单地检查 i = 0 到列表大小的循环

if ( strcmp(varAction[i].name, input) == 0)  {
    *varAction[i].pointer = condition;
}

Java 切换不止一次,是维护问题,所以我想要更好的方法。

我可以使用字符串的散列在数组中使用散列索引,但这会阻止我根据需要通过代码使用特定的变量名,从而使该代码不太清晰......即我不想要做(伪代码)

hashTable[varName] instead of (for example)  

if ( common.fracts )
     { do something }

一定有更好的方法吗?有没有?提前致谢。

【问题讨论】:

  • 我错过了什么吗?在我看来,'Map` 会完成你想要的。使用 HashMap 来实现。
  • @OleV.V.:这也是我的反应,但是 OP 说他/她不想在他/她提到这些属性的任何地方都使用字符串
  • FWIW,您通常可以使用多态性或按合同设计(接口)来解决对开关的需求,以封装您的开关语句正在执行的任何操作。这样,行为是对象类型所固有的,然后问题就减少到选择正确的具体类来实例化。
  • @AndrewC - 是的,我在这里的某个地方暗示过。对其他东西进行新设计是个好主意,但对于这个来说工作量太大了。感谢您的澄清。

标签: java c++ arrays hash switch-statement


【解决方案1】:

从你的问题来看,很明显你知道你不能做你说过你会在 C/C++ 中做的事情,但只是为了其他人提出这个问题:Java 没有对变量的引用。

把这个问题推到一个层次是很有诱惑力的:首先看看你为什么有varName,看看你是否可以避免它。

Java 切换不止一次,是维护问题,所以我想要更好的方法。

这表明common 应该具有此信息的访问器函数,因此开关只存在于一个地方(getter)或两个地方(getter 和 setter)。

void setThingy(String name, boolean value) {
    switch (name.toLowerCase())  {
        case "steps":
            this.steps = value;
            break;
        case "scale":
            this.scale = value;
            break;
        case "float":
            this.fracts = value;
            break;
        case "fraction":
            this.fracts = value;
            break;
        case "spaces":
            this.spaces = value;
            break;
        // ... etc.
    }
}

boolean getThingy(String name) {
    switch (name.toLowerCase())  {
        case "steps":
            return common.steps;
        case "scale":
            return common.scale;
        case "float":
            return this.fracts;
        case "fraction":
            return this.fracts;
        case "spaces":
            return this.spaces;
        // ... etc.
    }
}

如果 common 的类不是您可以更改的,那么某个地方的静态实用程序函数也可以工作,但如果可能的话,最好在类中。

【讨论】:

  • 谢谢@T.J. Crowder,我在其他情况下也这样做了,但在这种情况下可能会变得难看——我的错是我不清楚在许多源文件中如此频繁地使用变量,使用该函数会使代码变得不那么清晰。我想我曾经读过一些关于用于执行此操作的接口的内容,但我看不到与开发开销和清晰度损失相比的好处。
  • 根据带有“属性”名称的字符串的来源,我会更进一步,使用enum 类型而不是字符串。
  • 感谢@TJ,这似乎是目前最实用的.. 因为我意识到我只需要“put”部分.. vars 已经根据需要使用了。但我认为这与哈希映射的某种组合将在稍后完成。
【解决方案2】:

在 Java 8+ 中,你可以使用类似的东西:

Map<String, Runnable> actions = new HashMap<> ();
actions.put("steps", () -> common.steps = true);
actions.put("scale", () -> common.scales = true);
//etc.

然后在您的代码中:

actions.get(varName.toLowerCase()).run(); //need null check

您也可以使用 Java 7 来做到这一点——也可以使用匿名类,但会更冗长。

【讨论】:

  • 肯定比开关更容易看。
  • 这是我最初的想法,但我不确定这是不是最好的方法。我开始认为它可能是。除了一件事:据我所知,我不熟悉您用于键值的语法。“ () -> ...”部分是什么括号和箭头?
【解决方案3】:

这是一个选项,从评论中发展我的想法只是一点点。不知道你是否喜欢,我想提供它以防万一。

public class Common {

    private Map<String, Boolean> options = new HashMap<>();

    public void setOption(String varName, boolean condition) {
        options.put(varName.toLowerCase(), condition);
    }

    public boolean isSteps() { return options.get("steps"); }
    public boolean isFracts() { return options.get("fractions"); }
    public boolean isScale() { return options.get("scale"); }

}

您可能想要进行一些防御,例如避免设置不存在的选项。

编辑:借鉴 David Foerster 在枚举上的 cmets,这是使用它们的解决方案:

public enum Option {

    steps, scale, fraction;

    private boolean option = false;

    public static void setOption(String varName, boolean condition) {
        valueOf(varName.toLowerCase()).option = condition;
    }

    public boolean isSet() {
        return option;
    }

}

现在查找发生在 setter 中,而不是在 getter 中。对设置不存在的选项的防御是内置的:如果你尝试,你会得到一个异常,如果你愿意,当然可以修改这个行为。该解决方案具有很强的可扩展性,如果需要,可以轻松添加更多枚举常量。

【讨论】:

  • 一个有趣的可能性。
  • 根据带有“属性”名称的字符串的来源,我会更进一步,使用enum 类型而不是允许使用EnumSet 的字符串——开销要少得多比HashMap&lt;String, Boolean&gt;。您还可以使用#valueOf(String) 类方法派生适当的枚举值。
  • 枚举方法当然有一些优势。每个枚举实例甚至可以在其中包含布尔值,因此您可以在不查找的情况下执行if (Options.steps.isSet())。并且对设置不存在的选项的防御是内置的。
  • 枚举常量按照惯例应该是大写的。
  • @shmosel,正确。所以我们需要.toUpperCase()而不是setOption()中的.toLowerCase()
【解决方案4】:

警告:我上次写一些 Java 已经有一段时间了,这可能违反了最佳实践,所以请继续小心!也是just a quick and dirty example,我在(不是最新的)手机上写的……

你可以尝试使用反射:

class Common {
  public boolean a;
  public boolean b;
  public boolean tryToSet(String field, boolean value) throws java.lang.Exception {
    Class<?> cl = this.getClass();
    try {
      Field f = cl.getDeclaredField(field);
      f.setBoolean(this, value);
      return true;
    } catch(NoSuchFieldException e) {
      return false;
    }
  }
}

返回一个布尔值使您可以实现“默认情况”:

if (! c.tryToSet("x", false)) {
  System.out.println("some default case");
}

【讨论】:

  • 现在我们只需要一个 JNI 解决方案。
  • 感谢@Danier Jour。在我早期的 Java(1999 年)中,我做了这样的事情来编写一个通用的字段填充器。酷。
【解决方案5】:

试试这个:

import java.util.Scanner;

public class TestCases 
{
    static boolean steps = false;
    static boolean fracts;
    static boolean scale;

    public static void  main( String[] args ) 
    {
        Scanner input = new Scanner( System.in );

        System.out.println( "Type the input" );
        String typedInput = input.nextLine();

        Object[][] tests = { { "steps", steps }, { "float", fracts }, { "scale", scale } };

        for( int i = 0; i < tests.length; i++ )
        {
            if( typedInput.equals( tests[ i ][ 0 ] ) )
            {
                tests[ i ][ 1 ] = true;
                break;
            }
        }

        for( int i = 0; i < tests.length; i++ )
        {
            for( int j = 0; j < tests[ i ].length; j++ )
            {
                    System.out.print( tests[ i ][ j ] + " " );
            }
            System.out.println();
        }


    }

}

所有必要的转换都会自动完成。

【讨论】:

  • 谢谢 - 我从没想过要把它全部变成对象类型。非常接近哈希逻辑但更简单。该代码有效。
  • 我很高兴它对你有用。您可以将其标记为答案吗?或者投票给其他人看。
  • 这对我不起作用?我输入steps。最后的循环打印出steps是真的(如预期的那样),但开头声明的steps变量仍然是false
  • 当然@funaquarius24 但我是新来的,不确定我做对了。如果您没有看到,请告诉我或告诉我如何操作。
  • 当您将 steps(值为 false)放入 tests 时,它会自动转换为存储在 Object 数组中的对 Boolean.FALSE 的引用。在循环中设置tests[i][1] = true 时,数组中的引用更改为Boolean.TRUE,但原来的boolean steps 保持不变。因此,在 if (steps) 中引用它不会按预期工作。
猜你喜欢
  • 2021-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-16
  • 1970-01-01
  • 2023-03-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多