276
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.

Jasperan
  • 2,154
  • 1
  • 16
  • 40
hudi
  • 15,555
  • 47
  • 142
  • 246

15 Answers15

374

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

Stefano Cordio
  • 1,687
  • 10
  • 20
Jesper
  • 202,709
  • 46
  • 318
  • 350
  • 21
    You can also use enums in switch statements. – joriki Mar 29 '13 at 18:23
  • 31
    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? – Luan Nico Nov 02 '13 at 11:20
  • 14
    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. – Reimius Jun 26 '14 at 18:36
  • 4
    @Reimius 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. – Joffrey May 18 '15 at 22:46
  • Answer to a similar question https://stackoverflow.com/a/19931550/17428 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." – Eduard Wirch Jun 18 '19 at 09:35
  • [This might change soon.](https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001910.html) – Mordechai Jan 09 '20 at 05:07
144
switch ((i != null) ? i : DEFAULT_VALUE) {
        //...
}
tetsuo
  • 10,726
  • 3
  • 32
  • 35
48

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.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
assylias
  • 321,522
  • 82
  • 660
  • 783
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.
Shehzad
  • 2,870
  • 17
  • 21
  • 3
    The javadocs at your link no longer say "The prohibition against using null as a switch label [etc.]". – Patrick M Nov 27 '19 at 20:31
  • 3
    [This might change soon.](https://mail.openjdk.java.net/pipermail/amber-spec-experts/2020-January/001910.html) – Mordechai Jan 09 '20 at 05:08
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).

Beez
  • 2,081
  • 1
  • 20
  • 23
8

Pattern matching with switch

The null-behavior of switch statements is about to change with the addition of pattern matching (which has preview state in JDK 17/18/19/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 433: Pattern Matching for switch (Fourth Preview)

This means basically that you can simply write

switch (null) {
case null: ...
}

but if you omit the case null:-part, the switch will still throw a NullPointerException

Qw3ry
  • 1,319
  • 15
  • 31
5

You have to make a

if (i == null) {
   doSomething0();
} else {
   switch (i) {
   }
}
Kai
  • 38,985
  • 14
  • 88
  • 103
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...

Emmanuel Touzery
  • 9,008
  • 3
  • 65
  • 81
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 {
    // ...
}
Tankki3
  • 152
  • 6
  • 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) { }` – OneCricketeer Aug 09 '21 at 17:26
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

oikonomopo
  • 4,025
  • 7
  • 44
  • 73
3

Based on @tetsuo answer, with java 8 :

Integer i = ...

switch (Optional.ofNullable(i).orElse(DEFAULT_VALUE)) {
    case DEFAULT_VALUE:
        doDefault();
        break;    
}
Calfater
  • 1,255
  • 1
  • 10
  • 19
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.

Mikita Belahlazau
  • 15,326
  • 2
  • 38
  • 43
  • 4
    You can use enums too. – Karu Oct 21 '13 at 05:08
  • 6
    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 clause –  Nov 12 '13 at 14:07
  • 1
    @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). – Ben Mar 05 '14 at 02:59
  • 2
    I think he meant the default clause should handle null as any other possible enum value that was not caught by the previous case's – Leo Mar 06 '14 at 04:05
2
switch (String.valueOf(value)){
    case "null":
    default: 
}
Royz
  • 145
  • 1
  • 15
l0v3
  • 963
  • 7
  • 26
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.
Puneet
  • 11
  • 3
-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.

Abolfazl Mohajeri
  • 1,734
  • 2
  • 14
  • 26