Effective Java 第三版——38. 使用接口模拟可扩展的枚举

小说:手机派派怎么赚钱作者:乙建开更新时间:2018-11-21字数:62098

但由此可见,金刚琢与芭蕉扇这两样法宝结合起来使用,还真是难破。兵器法宝一并使金刚琢收来,再顺便来两扇子,便将对手吹出几万里去,这仗还怎么打?

未来最挣钱的五大行业

韩非早已从那些士绅嘴里得知这个牛大疤是个水中好手,称霸太湖水域数十年,而且是个很讲义气的水匪,跟其他专门打家劫舍的土匪不太一样,而且还严词拒绝了小鬼子的数次劝降,就冲这一点,韩非就觉得很佩服,这样的水匪可以争取过来打鬼子的。
刘皓马上回过神来看着这个女人那狡黠的目光哪里还不知道这根本不是她的名字,显然是她随意说的,不然的话自己绝对听不到。

紧接着,它的身体不再挣扎,而是开始了急速的颤抖,唐三那插在它身上的八蛛矛瞬间变成了惨白色,似乎有一股股的光晕不断顺着那只人面魔蛛的身体通过八蛛矛被吸入唐三体内。

Effective Java 第三版——38. 使用接口模拟可扩展的枚举


Tips
《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化。
在这里第一时间翻译成中文版。供大家学习分享之用。

Effective Java, Third Edition

38. 使用接口模拟可扩展的枚举

在几乎所有方面,枚举类型都优于本书第一版中描述的类型安全模式[Bloch01]。 从表面上看,一个例外涉及可扩展性,这在原始模式下是可能的,但不受语言结构支持。 换句话说,使用该模式,有可能使一个枚举类型扩展为另一个; 使用语言功能特性,它不能这样做。 这不是偶然的。 大多数情况下,枚举的可扩展性是一个糟糕的主意。 令人困惑的是,扩展类型的元素是基类型的实例,反之亦然。 枚举基本类型及其扩展的所有元素没有好的方法。 最后,可扩展性会使设计和实现的很多方面复杂化。

也就是说,对于可扩展枚举类型至少有一个有说服力的用例,这就是操作码( operation codes),也称为opcodes。 操作码是枚举类型,其元素表示某些机器上的操作,例如条目 34中的Operation类型,它表示简单计算器上的功能。 有时需要让API的用户提供他们自己的操作,从而有效地扩展API提供的操作集。

幸运的是,使用枚举类型有一个很好的方法来实现这种效果。基本思想是利用枚举类型可以通过为opcode类型定义一个接口,并实现任意接口。例如,这里是来自条目 34的Operation类型的可扩展版本:

// Emulated extensible enum using an interface
public interface Operation {
    double apply(double x, double y);
}


public enum BasicOperation implements Operation {
    PLUS("+") {
        public double apply(double x, double y) { return x + y; }
    },
    MINUS("-") {
        public double apply(double x, double y) { return x - y; }
    },
    TIMES("*") {
        public double apply(double x, double y) { return x * y; }
    },
    DIVIDE("/") {
        public double apply(double x, double y) { return x / y; }
    };
    private final String symbol;


    BasicOperation(String symbol) {
        this.symbol = symbol;
    }


    @Override public String toString() {
        return symbol;
    }
}

虽然枚举类型(BasicOperation)不可扩展,但接口类型(Operation)是可以扩展的,并且它是用于表示API中的操作的接口类型。 你可以定义另一个实现此接口的枚举类型,并使用此新类型的实例来代替基本类型。 例如,假设想要定义前面所示的操作类型的扩展,包括指数运算和余数运算。 你所要做的就是编写一个实现Operation接口的枚举类型:

// Emulated extension enum
public enum ExtendedOperation implements Operation {
    EXP("^") {
        public double apply(double x, double y) {
            return Math.pow(x, y);
        }
    },
    REMAINDER("%") {
        public double apply(double x, double y) {
            return x % y;
        }
    };

    private final String symbol;

    ExtendedOperation(String symbol) {
        this.symbol = symbol;
    }

    @Override public String toString() {
        return symbol;
    }
}

只要API编写为接口类型(Operation),而不是实现(BasicOperation),现在就可以在任何可以使用基本操作的地方使用新操作。请注意,不必在枚举中声明apply抽象方法,就像您在具有实例特定方法实现的非扩展枚举中所做的那样(第162页)。 这是因为抽象方法(apply)是接口(Operation)的成员。

不仅可以在任何需要“基本枚举”的地方传递“扩展枚举”的单个实例,而且还可以传入整个扩展枚举类型,并使用其元素。 例如,这里是第163页上的一个测试程序版本,它执行之前定义的所有扩展操作:

public static void main(String[] args) {
    double x = Double.parseDouble(args[0]);
    double y = Double.parseDouble(args[1]);
    test(ExtendedOperation.class, x, y);
}


private static <T extends Enum<T> & Operation> void test(
        Class<T> opEnumType, double x, double y) {
    for (Operation op : opEnumType.getEnumConstants())
        System.out.printf("%f %s %f = %f%n",
                          x, op, y, op.apply(x, y));
}

注意,扩展的操作类型的类字面文字(ExtendedOperation.class)从main方法里传递给了test方法,用来描述扩展操作的集合。这个类的字面文字用作限定的类型令牌(条目 33)。opEnumType参数中复杂的声明(<T extends Enum<T> & Operation> Class<T>)确保了Class对象既是枚举又是Operation的子类,这正是遍历元素和执行每个元素相关联的操作时所需要的。

第二种方式是传递一个Collection<? extends Operation>,这是一个限定通配符类型(条目 31),而不是传递了一个class对象:

public static void main(String[] args) {
    double x = Double.parseDouble(args[0]);
    double y = Double.parseDouble(args[1]);
    test(Arrays.asList(ExtendedOperation.values()), x, y);
}

private static void test(Collection<? extends Operation> opSet,
        double x, double y) {
    for (Operation op : opSet)
        System.out.printf("%f %s %f = %f%n",
                          x, op, y, op.apply(x, y));
}

生成的代码稍微不那么复杂,test方法灵活一点:它允许调用者将多个实现类型的操作组合在一起。另一方面,也放弃了在指定操作上使用EnumSet(条目 36)和EnumMap(条目 37)的能力。

上面的两个程序在运行命令行输入参数4和2时生成以下输出:

4.000000 ^ 2.000000 = 16.000000
4.000000 % 2.000000 = 0.000000

使用接口来模拟可扩展枚举的一个小缺点是,实现不能从一个枚举类型继承到另一个枚举类型。如果实现代码不依赖于任何状态,则可以使用默认实现(条目 20)将其放置在接口中。在我们的Operation示例中,存储和检索与操作关联的符号的逻辑必须在BasicOperationExtendedOperation中重复。在这种情况下,这并不重要,因为很少的代码是冗余的。如果有更多的共享功能,可以将其封装在辅助类或静态辅助方法中,以消除代码冗余。

该条目中描述的模式在Java类库中有所使用。例如,java.nio.file.LinkOption枚举类型实现了CopyOptionOpenOption接口。

总之,虽然不能编写可扩展的枚举类型,但是你可以编写一个接口来配合实现接口的基本的枚举类型,来对它进行模拟。这允许客户端编写自己的枚举(或其它类型)来实现接口。如果API是根据接口编写的,那么在任何使用基本枚举类型实例的地方,都可以使用这些枚举类型实例。

当前文章:http://0477auto.com/news/2018082537795/index.html

发布时间:2018-11-21 06:48:29

赚人民币的网游 梦幻西游商人一星期赚 天天趣闻真的能赚钱吗? yy挂机赚钱方法 广告手机挂机赚钱 手机做兼职赚钱 不同在家怎么利用 小投资餐饮小吃

编辑:帝文戏北

我要说两句: (0人参与)

发布