常量相关的方法

Java的enum有一个非常有趣的特性,即它允许程序员为enum实例编写方法,从而为每个enum实例赋予各自不同的行为。要实现常量相关的方法,你需要为enum定义一个或多个abstract方法,然后为每个enum实例实现该抽象方法。参考下面的例子:

public enum ConstantSpecificMethod {
    DATE_TIME{
        String getInfo() {
            return DateFormat.getDateInstance().format(new Date());
        }
    },
    CLASSPATH{
        String getInfo() {
            return System.getenv("CLASSPATH");
        }
    },
    VERSION{
        String getInfo() {
            return System.getProperty("java.version");
        }
    }
    ;
    abstract String getInfo();

    public static void main(String[] args) {
        for (ConstantSpecificMethod csm : values()) {
            System.out.println(csm.getInfo());
        }
    }
}

通过相应的enum实例,我们可以调用其上的方法。这通常也称为表驱动的代码(table-driven code, 请注意它与前面提到的命令模式的相似之处)。

在面向对象的程序设计中, 不同的行为与不同的类关联。而通过常量相关的方法,每个enum实例可以具备自己独特的行为,这似乎说明每个enum实例就像一个独特的类。在上面的例子中,enum实例似乎被当作其"超类"ConstantSpecificMethod来使用,在调用getInfo()方法时,体现出多态的行为。

然而,enum实例与类的相似之处也仅限于此了。我们并不能真的将enum实例作为一个类型来使用:

enum LikeClasses {
    WINKEN {
        void behavior() {
            System.out.println("Behavior1");
        }
    },
    BLINKEN{
        void behavior(){
            System.out.println("Behavior2");
        }
    },
    NOD{
        void behavior() {
            System.out.println("Behavior3");
        }
    };

    abstract void behavior();
}
/*
jiangxuedeMacBook-Pro:enum5 jianglei$ javap -c LikeClasses.class
Compiled from "NotClasses.java"
abstract class com.jianglei.enum5.LikeClasses extends java.lang.Enum<com.jianglei.enum5.LikeClasses> {
  public static final com.jianglei.enum5.LikeClasses WINKEN;

  public static final com.jianglei.enum5.LikeClasses BLINKEN;

  public static final com.jianglei.enum5.LikeClasses NOD;

*/

在方法f1()中,编译器不允许我们将一个enum实例当作class类型。如果我们分析一下编译器生成的代码,就知道这种行为也是很正常的。因为每个enum元素都是一个LinkClasses类型的static final实例。

同时,由于它们是static实例,无法访问外部类的非static元素或方法,所以对于内部的enum的实例而言,其行为与一般的内部类并不相同。

再看一个更有趣的关于洗车的例子。每个顾客在洗车时,都有一个选择菜单,每个选择对应一个不同的动作。可以将一个常量相关的方法关联到一个选择上,再使用一个EnumSet来保存客户的选择:

public class CarWash {
    public enum Cycle {
        UNDERBODY{
            @Override
            void action() {
                System.out.println("Spraying the underbody");
            }
        },
        WHEELWASH{
            @Override
            void action() {
                System.out.println("Washing the wheels");
            }
        },
        PREWASH{
            @Override
            void action() {
                System.out.println("Loosening the dirt");
            }
        },
        BASIC{
            @Override
            void action() {
                System.out.println("The basic wash");
            }
        },
        HOTWAX{
            void action() {
                System.out.println("Applying hot wax");
            }
        },
        RINSE{
            @Override
            void action() {
                System.out.println("Rinsing");
            }
        },
        BLOWDRY{
            void action() {
                System.out.println("Blowing dry");
            }
        };

        abstract void action();
    }

    EnumSet<Cycle> cycles =
            EnumSet.of(Cycle.BASIC, Cycle.RINSE);

    public void add(Cycle cycle) {
        cycles.add(cycle);
    }
    public void washCar() {
        for (Cycle cycle : cycles) {
            cycle.action();
        }
    }
    @Override
    public String toString() {
        return cycles.toString();
    }

    public static void main(String[] args) {
        CarWash wash = new CarWash();
        System.out.println(wash);
        wash.washCar();
        wash.add(Cycle.BLOWDRY);
        wash.add(Cycle.BLOWDRY);
        wash.add(Cycle.RINSE);
        wash.add(Cycle.HOTWAX);
        System.out.println(wash);
        wash.washCar();
    }
}
/*
[BASIC, RINSE]
The basic wash
Rinsing
[BASIC, HOTWAX, RINSE, BLOWDRY]
The basic wash
Applying hot wax
Rinsing
Blowing dry

*/

与使用匿名内部类相比较,定义常量相关方法语法更高效,简洁。 这个例子也展示了EnumSet了一些特性。因为它是一个集合,所以寻于同一个元素而言,只能出现一次,因此对同一个参数重复地调用add()方法会被忽略掉(这是正确的行为,因为一个bit位开关只能"打开"一次)。同样地,向EnumSet添加enum实例的顺序并不重要,因为其输出的次序决定于enum实例定义时的次序。 除实现abstract方法以外,程序员是否可以覆盖常量相关的方法呢?答案是肯定的,参考下面的程序:

public enum  OverrideConstantSpecific {
    NUT, BOLT,
    WASHER{
        void f() {
            System.out.println("Overriden method");
        }
    };

    void f() {
        System.out.println("default behavior");
    }

    public static void main(String[] args) {
        for (OverrideConstantSpecific ocs : values()) {
            System.out.println(ocs + ": ");
            ocs.f();
        }
    }
}

虽然enum有些限制,但是一般而言,我们还是可以将其看作是类。

results matching ""

    No results matching ""