7

I'm trying to initialize two variables with an enhanced switch statement:

int num = //something

boolean val1;
String val2;

val1, val2 = switch(num) {
    case 0 -> (true, "zero!");
    case 1 -> (true, "one!");
    default -> (false, "unknown :/");
}

Is this possible?

Allen Liao
  • 91
  • 5

5 Answers5

6

Since you are already on Java-13, I would suggest refraining from using an additional library to represent a tuple and make use ofMap.entry(introduced in Java-9) with the syntactic sugar of local variable type var inferred.

var entry = switch (num) {
    case 0 -> Map.entry(true, "zero!");
    case 1 -> Map.entry(true, "one!");
    default -> Map.entry(false, "unknown :/");
};
boolean val1 = entry.getKey();
String val2 = entry.getValue();
Naman
  • 27,789
  • 26
  • 218
  • 353
  • 1
    Nice, I didn't know about the `Map.entry()` shortcut... I don't know why I always hesitate to use `Entry` for holding arbitrary data (outside the context of a "map")... – ernest_k Feb 12 '20 at 04:58
  • 1
    @ernest_k but you need to keep in mind that `Map.entry()` has the same strict policy regarding `null` as `Map.of()`. – Holger Feb 12 '20 at 14:56
4

There’s not necessarily a benefit in cramming the initialization of two variables into one statement. Compare with

var val1 = switch(num) { case 0, 1 -> true; default -> false; };
var val2 = switch(num) { case 0 -> "zero!"; case 1 -> "one!"; default -> "unknown :/"; };

But for completeness, the new switch syntax does allow assignments too:

boolean val1;
String val2;
switch(num) {
    case 0 -> { val1 = true; val2 = "zero!"; }
    case 1 -> { val1 = true; val2 = "one!"; }
    default -> { val1 = false; val2 = "unknown :/"; }
}

You could also use the expression form to provide the initializer to one variable and assign the other

boolean val1;
String val2 = switch(num) {
    case 0 -> { val1 = true; yield "zero!"; }
    case 1 -> { val1 = true; yield "one!"; }
    default -> { val1 = false; yield "unknown :/"; }
};

but I wouldn’t be surprised if you don’t like it. For this specific example, it would also work to just use

var val2 = switch(num) { case 0 -> "zero!"; case 1 -> "one!"; default -> "unknown :/"; };
var val1 = !val2.equals("unknown :/");
Holger
  • 285,553
  • 42
  • 434
  • 765
  • Well, to start off a discussion, there is a benefit of wrapping those two attributes such as not losing the coupling when required to retrieve for example. Of course, that said, I am not denying a simple `class` defined to solve the purpose either would have worked providing additional extensibility. Also, the current approach that the OP has made use of, should ideally `return` the tuple to make more sense out of a method performing the `switch`. But yeah, that might just need a bit more elaboration over the requirement and design. – Naman Feb 12 '20 at 16:14
  • 2
    @Naman when coupling the values is the primary concern (rather than initializing the variables), a data structure would be appropriate, but then, don’t stop halfway, but create a static final Map containing these objects and replace the switch statement/expression by a map lookup… It would be even justified to create a dedicated type encapsulating a `boolean` and `String` (and perhaps even more) rather than using generic `Pair` or `Entry` objects. – Holger Feb 12 '20 at 16:17
  • 1
    I couldn't agree more, and I would stay with the lines that it would require more context around the problem the OP might be solving to get to understand the design and then decide holistically...A similar direction that I was looking towards when I termed `class` in the previous comment. – Naman Feb 12 '20 at 16:21
3

There's no tuple unpacking in Java. A quick alternative that still uses a switch expression could use a custom class (using Pair in the following example):

Pair<Boolean, String> val = switch (num) {
    case 0 -> Pair.of(true, "zero!");
    case 1 -> Pair.of(true, "one!");
    default -> Pair.of(false, "unknown :/");
};

boolean val1 = val.getLeft();
String val2 = val.getRight();
ernest_k
  • 44,416
  • 5
  • 53
  • 99
  • @AllenLiao though there is [no need of using an additional library](https://stackoverflow.com/a/60180440/1746118) for this. – Naman Feb 12 '20 at 02:59
  • 1
    `boolean val1 = num == 0 || num == 1; String val2 = val1? List.of("zero!", "one!").get(num): "unknown :/";` – Holger Feb 12 '20 at 14:55
  • 1
    @Naman whether you use `Map.entry(true, "zero!")` or `Pair.of(true, "zero!");`, I’m not sure whether this is a significant benefit over `{ val1 = true; val2 = "zero!"; }` (compare with [my answer](https://stackoverflow.com/a/60191823/2711488)), given that the values have to be extracted in two additional statements then (or whether this benefit, if there is one, justifies the expenses of auto-boxing and creation of an ephemeral wrapper instance). – Holger Feb 12 '20 at 15:43
1

Short answer, no, java does not support multiple assignment in any way as far as I'm aware. A workaround could be to buld a container class in witch to store both a string and bool.

example

class Response 
{
    String message;
    boolean found;
}

and return a object containing both.

Or potentially you could use an array and type casting, or a differently structured statement.


switch(num) {
    case 0: 
        val1 = true;
        val2 = "zero!"
        break;
    case 1: 
        val1 = true;
        val2 = "one!"
        break;
    default: 
        val1 = false;
        val2 = "unknown :/"
        break;
}
Dorian Gray
  • 2,913
  • 1
  • 9
  • 25
flytex
  • 98
  • 7
1

Another option (introduced as a preview feature in Java 14 and became a standard feature since Java 16) is to use a record as the container of the two values you wish to return.

This reduces the boilerplate code you'd have to write in order to create a regular class that holds the two values, while providing better readability compared to using the Map.Entry or Pair classes.

record Something (boolean boolField, String strField) {};

var result = switch(num) {
    case 0 -> new Something (true, "zero!");
    case 1 -> new Something (true, "one!");
    default -> new Something (false, "unknown :/");
};
boolean val1 = result.boolField ();
String val2 =  result.strField ();

Note that instead of Something, boolField and strField you should use meaningful names that describe the purpose of this record and its fields.

Eran
  • 387,369
  • 54
  • 702
  • 768