Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complete switch on enum in Java produces "Missing return statement" error

Suppose, we have switch statement, that fully covers all possible cases of enum parameter, and has null-check too, will not be compiled cause of "Missing return statement".

enum Foo {ONE,TWO}

int fooToInt(Foo foo) {
    if (foo == null) {
        throw new NullPointerException();
    }
    switch (foo) {
        case ONE: return 1;
        case TWO: return 2;
    }
}

I know, that throwing exception from default case or after enum, or visiting enum element instead of switch will fix the problem. But I do not understand technical cause of such behavior: obviously, there is no possible branch of execution, which not leads to return or throw. Also it would be great in some cases to have compile-time check that all cases are covered.

like image 734
Adamovskiy Avatar asked Aug 30 '16 10:08

Adamovskiy


People also ask

How do I fix missing return statement in Java?

How to resolve the error? In order to solve the missing return statement error, we simply need to add the return statement to the method just like to the one we did in case one. So, we will return the some value of the same type which we used before the name like as: public static String checkNumber( int number) {

Can you use a switch statement around an enum in Java?

We can use also use Enum keyword with Switch statement. We can use Enum in Switch case statement in Java like int primitive.

What does missing return statement mean in Java?

The “missing return statement” message occurs when a method does not have a return statement. Each method that returns a value (a non-void type) must have a statement that literally returns that value so it can be called outside the method.

Can we return enum in Java?

Java Enum valueOf()The valueOf() method takes a string and returns an enum constant having the same string name. For example, Size. valueOf("SMALL") // returns constant SMALL.


2 Answers

I'm going to take a shot in the dark here, having not read any rationale for this, but if this is not the primary reason for the behavior, it is at least a reason.

Suppose that the enum instead comes from a library which your project depends on. In the version you compile against, ONE and TWO are the only options. However, you may end up running against a later version (through OSGi or other solutions) that has added another value THREE. If THREE gets passed to fooToInt, it will reach the end of your method, and nothing will have been returned (or thrown). Oops.

This is rather unpleasant to discover at runtime, so you are forced to make a choice as to how it will be handled, even if it actually seems impossible at compile time. Some cases, like the one in your example, could probably be detected and allowed to compile, and other cases could have been handled differently (e.g. an implicit throw), but on the list of all things that could be done to improve Java, I wouldn't put that at the top.

like image 103
Jacob Raihle Avatar answered Sep 21 '22 15:09

Jacob Raihle


Since you did not write default, the compiler added it automatically at the next line after switch block. At that point, the compiler "notice" that there is no return point from the method and gives you that error.

I've taken you example and changed but added throw of RuntimeException after the switch, like this:

public class Example {

   enum Foo { ONE, TWO }

    int fooToInt(Foo foo) {
        if (foo == null) {
            throw new NullPointerException();
        }
        switch (foo) {
            case ONE: return 1;
            case TWO: return 2;
        }

        throw new RuntimeException("Should not have gotten here");
    }

    public static void main(String[] args) {

    }
}

I've compiled the class and used javap -c Example.class to see the actual bytecode (see below). Notice the "default: 52" which was added by the javac. It leads to the block section after the switch case, and there, I've thrown RuntimeException which overwrite the need for return.

Compiled from "Example.java"
public class com.mprv.automation.jenkins.Example {
  public com.mprv.automation.jenkins.Example();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  int fooToInt(com.mprv.automation.jenkins.Example$Foo);
    Code:
       0: aload_1
       1: ifnonnull     12
       4: new           #2                  // class java/lang/NullPointerException
       7: dup
       8: invokespecial #3                  // Method java/lang/NullPointerException."<init>":()V
      11: athrow
      12: getstatic     #4                  // Field com/mprv/automation/jenkins/Example$1.$SwitchMap$com$mprv$automation$jenkins$Example$Foo:[I
      15: aload_1
      16: invokevirtual #5                  // Method com/mprv/automation/jenkins/Example$Foo.ordinal:()I
      19: iaload
      20: lookupswitch  { // 2
                     1: 48
                     2: 50
               default: 52
          }
      48: iconst_1
      49: ireturn
      50: iconst_2
      51: ireturn
      52: new           #6                  // class java/lang/RuntimeException
      55: dup
      56: ldc           #7                  // String Should not have gotten here
      58: invokespecial #8                  // Method java/lang/RuntimeException."<init>":(Ljava/lang/String;)V
      61: athrow

  public static void main(java.lang.String[]);
    Code:
       0: return
}
like image 42
Tzach Solomon Avatar answered Sep 22 '22 15:09

Tzach Solomon