How to use null in switch
Asked Answered
L

15

287
Integer i = ...
    
switch (i) {
    case null:
        doSomething0();
        break;    
}

In the code above I can't use null in the switch case statement. How can I do this differently? I can't use default because then I want to do something else.

Leaguer answered 26/4, 2012 at 11:4 Comment(3)
before switch check for null condition if(i==null) {//dosomething}Trajectory
This would actually make the switch useful. Other pattern matching languages work this way.Richards
in Java 21 or later you must only add an additional default case and it will workIncorporeal
I
390

This was not possible with a switch statement in Java until Java 18. You had to check for null before the switch. But now, with pattern matching, this is a thing of the past. Have a look at JEP 420:

Pattern matching and null

Traditionally, switch statements and expressions throw NullPointerException if the selector expression evaluates to null, so testing for null must be done outside of the switch:

static void testFooBar(String s) {
     if (s == null) {
         System.out.println("oops!");
         return;
     }
     switch (s) {
         case "Foo", "Bar" -> System.out.println("Great");
         default           -> System.out.println("Ok");
     }
 }

This was reasonable when switch supported only a few reference types. However, if switch allows a selector expression of any type, and case labels can have type patterns, then the standalone null test feels like an arbitrary distinction, and invites needless boilerplate and opportunity for error. It would be better to integrate the null test into the switch:

static void testFooBar(String s) {
    switch (s) {
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");   
  }
}

More about switch (including an example with a null variable) in Oracle Docs - Switch

Ingeborg answered 26/4, 2012 at 11:7 Comment(7)
You can also use enums in switch statements.Dipterous
It makes sense that you can't use a null Integer or other Wrapper class, because of unboxing. But what about enums and strings? Why can't they be null?Tug
I'm not understanding why a short circuit of null being mapped to the "default" case or a special case for a null switch was not implemented for Strings. It makes using switches to simplify code pointless since you always have to do a null check. I'm not saying simplification is the only use for switches though.Todd
@Todd you don't always have to do a null check. If you respect the code contracts that you give to your methods, you can almost always manage to not have your code cluttered with null checks. Using asserts is always nice, though.Helsie
Answer to a similar question https://mcmap.net/q/109872/-why-java-switch-statement-can-39-t-handle-null-since-it-has-a-quot-default-quot-clause-duplicate contains a JLS quote. It basically says "The prohibition against using null as a switch label prevents one from writing code that can never be executed."Maurine
This might change soon.Wandawander
@Todd if your null and default case matches, you can write it like this: case null, default -> System.out.println("Value is missing");Ungracious
T
150
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}
Takara answered 2/6, 2015 at 16:4 Comment(2)
cleaner way than using one extra if elseEuphrosyne
For Groovy, you can use switch (i ?: DEFAULT_VALUE)Duplessismornay
G
49

switch(i) will throw a NullPointerException if i is null, because it will try to unbox the Integer into an int. So case null, which happens to be illegal, would never have been reached anyway.

You need to check that i is not null before the switch statement.

Gapes answered 26/4, 2012 at 11:6 Comment(0)
V
26

Java docs clearly stated that:

The prohibition against using null as a switch label prevents one from writing code that can never be executed. If the switch expression is of a reference type, such as a boxed primitive type or an enum, a run-time error will occur if the expression evaluates to null at run-time.

You must have to verify for null before Swithch statement execution.

if (i == null)

See The Switch Statement

case null: // will never be executed, therefore disallowed.
Vise answered 26/4, 2012 at 11:14 Comment(2)
The javadocs at your link no longer say "The prohibition against using null as a switch label [etc.]".Preemption
This might change soon.Wandawander
U
15

Given:

public enum PersonType {
    COOL_GUY(1),
    JERK(2);

    private final int typeId;
    private PersonType(int typeId) {
        this.typeId = typeId;
    }

    public final int getTypeId() {
        return typeId;
    }

    public static PersonType findByTypeId(int typeId) {
        for (PersonType type : values()) {
            if (type.typeId == typeId) {
                return type;
            }
        }
        return null;
    }
}

For me, this typically aligns with a look-up table in a database (for rarely-updated tables only).

However, when I try to use findByTypeId in a switch statement (from, most likely, user input)...

int userInput = 3;
PersonType personType = PersonType.findByTypeId(userInput);
switch(personType) {
case COOL_GUY:
    // Do things only a cool guy would do.
    break;
case JERK:
    // Push back. Don't enable him.
    break;
default:
    // I don't know or care what to do with this mess.
}

...as others have stated, this results in an NPE @ switch(personType) {. One work-around (i.e., "solution") I started implementing was to add an UNKNOWN(-1) type.

public enum PersonType {
    UNKNOWN(-1),
    COOL_GUY(1),
    JERK(2);
    ...
    public static PersonType findByTypeId(int id) {
        ...
        return UNKNOWN;
    }
}

Now, you don't have to do null-checking where it counts and you can choose to, or not to, handle UNKNOWN types. (NOTE: -1 is an unlikely identifier in a business scenario, but obviously choose something that makes sense for your use-case).

Unlearned answered 2/10, 2015 at 21:48 Comment(1)
UNKNOWN is the best solution on this I've ever seen and overcomse nullchecks.Smashandgrab
C
11

Pattern matching with switch

The null-behavior of switch statements has changed with the addition of pattern matching, which arrived with JDK 21 (and has preview state in JDK 17-20)

Switches and null

Traditionally, switch statements and expressions throw NullPointerException if the selector expression evaluates to null, so testing for null must be done outside of the switch.

[...]

This was reasonable when switch supported only a few reference types. However, if switch allows a selector expression of any type, and case labels can have type patterns, then the standalone null test feels like an arbitrary distinction, and invites needless boilerplate and opportunity for error. It would be better to integrate the null test into the switch by allowing a new null case label.

See JEP 441: Pattern Matching for switch

This means basically that you can simply write

// As of Java 21
static void testFooBarNew(String s) {
    switch (s) {
        case null         -> System.out.println("Oops");
        case "Foo", "Bar" -> System.out.println("Great");
        default           -> System.out.println("Ok");
    }
}

Be aware though that you have to use case null explicitly:

To maintain backward compatibility with the current semantics of switch, the default label does not match a null selector.

Cung answered 2/6, 2022 at 11:1 Comment(0)
J
5

You have to make a

if (i == null) {
   doSomething0();
} else {
   switch (i) {
   }
}
Jelsma answered 26/4, 2012 at 11:7 Comment(0)
C
4

Some libraries attempt to offer alternatives to the builtin java switch statement. Vavr is one of them, they generalize it to pattern matching.

Here is an example from their documentation:

String s = Match(i).of(
    Case($(1), "one"),
    Case($(2), "two"),
    Case($(), "?")
);

You can use any predicate, but they offer many of them out of the box, and $(null) is perfectly legal. I find this a more elegant solution than the alternatives, but this requires java8 and a dependency on the vavr library...

Cassidy answered 26/5, 2016 at 12:13 Comment(0)
J
4

I just learned today that you dont have to put another layer of indentation/curly brackets just because of the if that is checking for null. You can do either of these:

if (i == null) {
    // ...
} else switch (i) {
    case 1:
        // ...
        break;
    default:
        // ...
}

or

if (i != null) switch (i) {
    case 1:
        // ...
        break;
    default:
        // ...
} else {
    // ...
}
Jamiejamieson answered 18/5, 2021 at 4:35 Comment(1)
This works because the switch is a block itself - you're effectively making a one-line if block here, and could just as easily have if (i == null) return defaultValue; switch (i) { }Auto
O
3

You can also use String.valueOf((Object) nullableString) like

switch (String.valueOf((Object) nullableString)) {
case "someCase"
    //...
    break;
...
case "null": // or default:
    //...
        break;
}

See interesting SO Q/A: Why does String.valueOf(null) throw a NullPointerException

Ophiolatry answered 6/5, 2018 at 12:0 Comment(0)
M
3

Based on @tetsuo answer, with java 8 :

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
Mciver answered 10/12, 2019 at 9:59 Comment(0)
C
2

You can't. You can use primitives (int, char, short, byte) and String (Strings in java 7 only) in switch. primitives can't be null.
Check i in separate condition before switch.

Claypool answered 26/4, 2012 at 11:6 Comment(4)
You can use enums too.Thereupon
if the enum is null, you'll have the same problem. BTW, it's pretty strange that switch can't handle null, since it has a default clauseTrod
@LeonardoKenji Default clause doesn't really have anything to do with null; whatever you're switching on it'll be dereferenced in order to check any other cases, so the default clause won't handle the null case (a NullPointerException is thrown before it has a chance to).Beep
I think he meant the default clause should handle null as any other possible enum value that was not caught by the previous case'sGermaun
P
2
switch (String.valueOf(value)){
    case "null":
    default: 
}
Perkoff answered 30/11, 2017 at 10:58 Comment(0)
K
0

Just consider how the SWITCH might work,

  • in case of primitives we know it can fail with NPE for auto-boxing
  • but for String or enum, it might be invoking equals method, which obviously needs a LHS value on which equals is being invoked. So, given no method can be invoked on a null, switch cant handle null.
Kra answered 18/6, 2019 at 10:24 Comment(0)
G
-1

Maybe this is one of possible solution:

Integer i = Optional.ofNullable(...).orElse(VALUE);

VALUE is your special value that can be in your switch statement.

Gimbals answered 15/4, 2023 at 7:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.