enum 的常量值添加 onymous class

enum's constant value adding onymous class

在enum的常量值声明的class体中添加onymous(非匿名)class方法有什么用?

public enum Status {
    SUCCESS("SUCCESS"){},FAILED("FAILED"){  
         class Test { 
               public void test() {
                  System.out.println("test");
               }
         }
    };
    private String code;

    Status(String code) {
        this.code = code;
    }

我如何access/execute这样的方法?我找到 示例,不推荐使用

As a recommendation, make your enum implement your interface to make the code more readable

我没有在 JLS 的枚举常量部分找到用法

The optional class body of an enum constant implicitly defines an anonymous class declaration (§15.9.5) that extends the immediately enclosing enum type. The class body is governed by the usual rules of anonymous classes; in particular it cannot contain any constructors.

Instance methods declared in these class bodies may be invoked outside the enclosing enum type only if they override accessible methods in the enclosing enum type.

TL;DR 很难想象在枚举常量内部定义内部 class 有意义的现实情况。

让我们从您的代码示例开始...

public enum Status {
    SUCCESS("SUCCESS") {

    },
    FAILED("FAILED") {  
        class Test { 
            public void test() {
                System.out.println("test");
            }
        }
    };
    private String code;

    Status(String code) {
        this.code = code;
    }
}

由于 FAILED 是一个带有主体的枚举值,它成为 Status 枚举 class 的匿名子 class。而 Test 是在这个匿名的 class 中定义的。由于其封闭 class 的匿名性质,无法从 FAILED 外部表达其名称。肯定不是 Status.FAILED.Test.

所以 Test 主要在 FAILED 内部有用(如果 FAILED 的实现足够复杂以保证内部 class)。通常,我希望枚举常量不要变得那么复杂,但这是风格问题。

FAILED 外部访问 Test 只能通过超级 class 或 Test 扩展/实现的接口,并且只能访问通过那个 superclass 或者 interface.

显示 FAILED 内部和外部用法的(人为的)示例可能是:

public class StatusTest {

    enum Status {
        FAILED{  
            class Test implements Runnable { 
                private String text = "Test " + System.currentTimeMillis();
                @Override
                public void run() {
                    System.out.println(text);
                }
            }
            @Override
            public Runnable getRunner() {
                return new Test();
            }
            @Override
            public void message() {
                getRunner().run();
            }
        };
        public abstract void message();
        public abstract Runnable getRunner();
    }

    public static void main(String[] args) {
        Status status = Status.FAILED;
        status.message();
        Runnable runner = status.getRunner();
        runner.run();
    }
}

(稍后添加)

当然,在此示例中,Runnable 没有理由应该获得 class 名称。我通常会使用命名的内部 class 而不是匿名的,只有当它是

  • 在多个地方使用或
  • 如此复杂以至于封闭的方法变得不可读。

在引入匿名与命名内部时,这总是相同的决定 class。对于枚举,给内部 class 一个名字的理由就更少了,因为这个名字在外面是不可用的。所以,如果我看到像上面这样的代码,我会重构它以使用匿名 class:

public class StatusTest {

    enum Status {
        FAILED { 
            @Override
            public Runnable getRunner() {
                return new Runnable() { 
                    private String text = "Test " + System.currentTimeMillis();
                    @Override
                    public void run() {
                        System.out.println(text);
                    }
                };
            }
            @Override
            public void message() {
                getRunner().run();
            }
        };
        public abstract void message();
        public abstract Runnable getRunner();
    }

    public static void main(String[] args) {
        Status status = Status.FAILED;
        status.message();
        Runnable runner = status.getRunner();
        runner.run();
    }
}

在这两种情况下,内部 class 本身对外部代码不可见,仅在枚举常量内部可见,并且如果枚举常量的实现变得如此复杂以至于需要一个命名的内部 class,我肯定会重构它,例如通过将复杂性委托给一些普通的顶级 class.