常量相关的方法(枚举类型):Thing In Java

常量相关的方法

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

package lime.tij._019._010._000;


import java.text.DateFormat;
import java.util.Date;

/**
 * @Author liangmy
 * @Date 2019/9/30
 */
public enum  ConstantSpecificMethod {
    DATE_TIME{
        @Override
        String getInfo(){
            return DateFormat.getDateInstance().format(new Date());
        }
    },
    CLASSPATH{
        @Override
        String getInfo(){
            return System.getenv("CLASSPATH");
        }
    },
    VERSION{
        @Override
        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实例作为一个类型来使用:

package lime.tij._019._010._000;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * @Author liangmy
 * @Date 2019/9/30
 */
enum LikeClasses {
    WINKEN {
        @Override
        void behavior() {
            System.out.println("Behavior1");
        }
    },
    BLINKEN {
        @Override
        void behavior() {
            System.out.println("Behavior2");
        }
    },
    NOD {
        @Override
        void behavior() {
            System.out.println("Behavior3");
        }
    };

    abstract void behavior();
}

public class NotClasses {
    //    void f1(LikeClasses.WINKEN instance){} // Nope
    public static void main(String[] args) throws IOException {
        String filePath = "/Users/liangmy/ideaProjects/lime/target/classes/lime/tij/_019/_010/_000/LikeClasses.class";
        ProcessBuilder processBuilder = new ProcessBuilder().command("javap", filePath);
        Process process = processBuilder.start();
        InputStream inputStream = process.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        for(String s;(s = bufferedReader.readLine()) != null;){
            System.out.println(s);
        }
        inputStream.close();
        process.destroy();
    }
}

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

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

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

package lime.tij._019._010._000;

import java.util.EnumSet;

/**
 * @Author liangmy
 * @Date 2019/9/30
 */
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 {
            @Override
            void action() {
                System.out.println("Applying hot wax");
            }
        },
        // 漱;冲洗掉;漂净
        RINSE {
            @Override
            void action() {
                System.out.println("Rinsing");
            }
        },
        // 吹;打击,猛击;挫折;强风;擤鼻子;
        BLOWDRY {
            @Override
            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 c : cycles) {
            c.action();
        }
    }

    @Override
    public String toString() {
        return cycles.toString();
    }

    public static void main(String[] args) {
        CarWash wash = new CarWash();
        System.out.println(wash);
        wash.washCar();
        // Order of addition is unimportant :
        wash.add(Cycle.BLOWDRY);
        wash.add(Cycle.BLOWDRY); // Duplicates ignored
        wash.add(Cycle.RINSE);
        wash.add(Cycle.HOTWAX);
        System.out.println(wash);
        wash.washCar();
    }
}

与使用匿名内部类相比较,定义常量相关方法的语法更高效、简洁。

这个例子也展示了EnumSet的一些特性。因为它是一个集合,所以对于同一个元素而言,只能出现一次,因此对同一个参数重复地调用add()方法会被忽略掉(这是正确的行为,因为一个bit为开关只能“打开”一次)。同样地,向EnumSet添加enum实例的顺序并不重要,因为其输出的次序决定于enum实例定义时的次序。

除了实现abstract方法外,程序员是否可以覆盖常量相关的方法呢?答案是肯定的,参考下面的程序:

package lime.tij._019._010._000;

/**
 * @Author liangmy
 * @Date 2019/9/30
 */
public enum OverrideConstantSpecific {
    NUT, BOLT,
    WASHER {
        @Override
        void f() {
            System.out.println("overridden method");
        }
    };

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值