🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 使用 enum 分发 直接将 RoShamBol.java 翻译为基于 enum 的版本是有问题的,因为 enum 实例不是类型,不能将 enum 实例作为参数的类型,所以无法重载 eval() 方法。不过,还有很多方式可以实现多路分发,并从 enum 中获益。 一种方式是使用构造器来初始化每个 enum 实例,并以“一组”结果作为参数。这二者放在一块,形成了类似查询表的结构: ```java // enums/RoShamBo2.java // Switching one enum on another // {java enums.RoShamBo2} package enums; import static enums.Outcome.*; public enum RoShamBo2 implements Competitor<RoShamBo2> { PAPER(DRAW, LOSE, WIN), SCISSORS(WIN, DRAW, LOSE), ROCK(LOSE, WIN, DRAW); private Outcome vPAPER, vSCISSORS, vROCK; RoShamBo2(Outcome paper, Outcome scissors, Outcome rock) { this.vPAPER = paper; this.vSCISSORS = scissors; this.vROCK = rock; } @Override public Outcome compete(RoShamBo2 it) { switch(it) { default: case PAPER: return vPAPER; case SCISSORS: return vSCISSORS; case ROCK: return vROCK; } } public static void main(String[] args) { RoShamBo.play(RoShamBo2.class, 20); } } ``` 输出为: ``` ROCK vs. ROCK: DRAW SCISSORS vs. ROCK: LOSE SCISSORS vs. ROCK: LOSE SCISSORS vs. ROCK: LOSE PAPER vs. SCISSORS: LOSE PAPER vs. PAPER: DRAW PAPER vs. SCISSORS: LOSE ROCK vs. SCISSORS: WIN SCISSORS vs. SCISSORS: DRAW ROCK vs. SCISSORS: WIN SCISSORS vs. PAPER: WIN SCISSORS vs. PAPER: WIN ROCK vs. PAPER: LOSE ROCK vs. SCISSORS: WIN SCISSORS vs. ROCK: LOSE PAPER vs. SCISSORS: LOSE SCISSORS vs. PAPER: WIN SCISSORS vs. PAPER: WIN SCISSORS vs. PAPER: WIN SCISSORS vs. PAPER: WIN ``` 在 compete() 方法中,一旦两种类型都被确定了,那么唯一的操作就是返回结果 Outcome 然而,你可能还需要调用其他的方法,(例如)甚至是调用在构造器中指定的某个命令对象上的方法。 RoShamBo2.javal 之前的例子短小得多,而且更直接,更易于理解。注意,我们仍然是使用两路分发来判定两个对象的类型。在 RoShamBol.java 中,两次分发都是通过实际的方法调用实现,而在这个例子中,只有第一次分发是实际的方法调用。第二个分发使用的是 switch,不过这样做是安全的,因为 enum 限制了 switch 语句的选择分支。 在代码中,enum 被单独抽取出来,因此它可以应用在其他例子中。首先,Competitor 接口定义了一种类型,该类型的对象可以与另一个 Competitor 相竞争: ```java // enums/Competitor.java // Switching one enum on another package enums; public interface Competitor<T extends Competitor<T>> { Outcome compete(T competitor); } ``` 然后,我们定义两个 static 方法(static 可以避免显式地指明参数类型),第一个是 match() 方法,它会为一个 Competitor 对象调用 compete() 方法,并与另一个 Competitor 对象作比较。在这个例子中,我们看到,match()方法的参数需要是 Competitor\<T\> 类型。但是在 play() 方法中,类型参数必须同时是 Enum\<T\> 类型(因为它将在 Enums.random() 中使用)和 Competitor\<T\> 类型(因为它将被传递给 match() 方法): ```java // enums/RoShamBo.java // Common tools for RoShamBo examples package enums; import onjava.*; public class RoShamBo { public static <T extends Competitor<T>> void match(T a, T b) { System.out.println( a + " vs. " + b + ": " + a.compete(b)); } public static <T extends Enum<T> & Competitor<T>> void play(Class<T> rsbClass, int size) { for(int i = 0; i < size; i++) match(Enums.random(rsbClass),Enums.random(rsbClass)); } } ``` play() 方法没有将类型参数 T 作为返回值类型,因此,似乎我们应该在 Class\<T\> 中使用通配符来代替上面的参数声明。然而,通配符不能扩展多个基类,所以我们必须采用以上的表达式。