枚举的常量值添加匿名类

时间:2019-02-23 19:25:04

标签: java enums anonymous-class

在枚举的常量值声明中的类主体中添加 onymous (不是匿名)类方法有什么用?

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;
    }

如何访问/执行这种方法?我找到anonymous class的示例,不推荐使用

  

作为建议,使您的枚举实现您的接口以使代码更具可读性

我在JLS的枚举常量部分中找不到用法

  

枚举常量的可选类主体隐式定义了一个匿名类声明(第15.9.5节),该声明扩展了立即封闭的枚举类型。类主体由匿名类的通常规则控制;特别是它不能包含任何构造函数。

     在这些类主体中声明的

实例方法只能在封闭枚举类型之外调用,除非它们覆盖了封闭枚举类型的可访问方法

1 个答案:

答案 0 :(得分:1)

TL; DR很难想象在现实世界中在枚举常量内部定义内部类是有意义的。

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

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枚举类的匿名子类。 Test是在此匿名类中定义的。由于其封闭类的匿名性质,因此无法从FAILED外部表达其名称。当然不是Status.FAILED.Test

因此,Test主要在FAILED内部有用(如果FAILED的实现足够复杂以至于需要一个内部类)。通常,我希望枚举常量不变得那么复杂,但这只是样式问题。

只有通过Test扩展/实现的超类或接口才能访问FAILED外部的Test,并且只能访问通过该超类或接口公开的方法。

一个展示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应该获得类名。我通常只使用命名内部类而不是匿名内部类

  • 在多个地方使用,或
  • 太复杂了,以至于使封装方法不可读。

在引入匿名类与命名内部类时,这始终是相同的决定。对于枚举,几乎没有理由给内部类起一个名字,因为该名称不能在外部使用。因此,如果我看到上面的代码,我将其重构为使用匿名类:

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();
    }
}

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