java只支持单路分发,如果要处理的参数包含了不知一个未知类型的对象时,那么java的动态绑定机制只能去处理一个类型。解决上面的办法就是自己去判断其他类型,从而实现自己的动态绑定。解决个问题的就是多路分发。
一、多路分发:
由于多态只发生在方法调用的时候,所以要是实现多路分发(以两路为列),就要有两个方法的调用,第一个方法 决定第一个未知说的类型,第二个方法决定第二个位置说的类型。
package com.package19.games;import java.util.Random;class Stone implements Item{@Overridepublic String toString() {return "石头";}@Overridepublic Outcom compaete(Item item) {System.out.println(item+"stone");return item.eval(this);}@Overridepublic Outcom eval(Stone stone) {System.out.println(stone);return Outcom.平局;}@Overridepublic Outcom eval(Scissors scissors) {System.out.println(scissors);return Outcom.胜利;}@Overridepublic Outcom eval(Cloth cloth) {System.out.println(cloth);return Outcom.失败;}//石头}class Scissors implements Item{@Overridepublic String toString() {return "剪刀";}@Overridepublic Outcom compaete(Item item) {System.out.println(item+"scissors");return item.eval(this);}@Overridepublic Outcom eval(Stone stone) {System.out.println(stone);return Outcom.失败;}@Overridepublic Outcom eval(Scissors scissors) {System.out.println(scissors);return Outcom.平局;}@Overridepublic Outcom eval(Cloth cloth) {System.out.println(cloth);return Outcom.胜利;}//剪刀}class Cloth implements Item{@Overridepublic Outcom compaete(Item item) {System.out.println(item+"cloth");return item.eval(this);}@Overridepublic Outcom eval(Stone stone) {System.out.println(stone);return Outcom.胜利;}@Overridepublic Outcom eval(Scissors scissors) {System.out.println(scissors);return Outcom.失败;}@Overridepublic Outcom eval(Cloth cloth) {System.out.println(cloth);return Outcom.平局;}@Overridepublic String toString() {return "布";}}public class Rondem_One {static final int SIZE=20;static Random random = new Random();public static Item gaems(){switch (random.nextInt(3)){default:case 0:return new Stone();case 1:return new Scissors();case 2:return new Cloth();}}public static void methods(Item item1,Item item2){Outcom outcom = item1.compaete(item2);System.out.println(item1+" VS "+item2+":"+ outcom);}public static void main(String[] args) {Item gaems = gaems();Item gaems1 = gaems();methods(gaems,gaems1);}}
上述石头剪刀布的列子就是一个多路分发,在methods(Item item1,Item item2)方法中有两个未知对象的类型,调用item1.compaete(item2),通过java的单路分发方法可以确定第一个对象的类型,然后每个类型的内部又有一个 compaete(Item item) 方法来确定第二个对象的类型。
二、使用enum实现分发
使用enum也可以做到分发。
1.产生随机enum实例的方法
public class Enums {private static Random ran=new Random();//1.通过一个方法对返回的获取class对象,然后在通过random()方法处理返回的数组public static <T extends Enum<T>> T random(Class<T> cla){return random(cla.getEnumConstants());//getEnumConstants()拿到enum类中的所有enum实例}//处理返回的数组public static <T> T random(T[] values){return values[ran.nextInt(values.length)];}//2.直接在方法内部一次进行完毕public static <T extends Enum<T>> T randoms(Class<T> s){T[] constants = s.getEnumConstants();T t=constants[ran.nextInt(constants.length)];return t;}}
2.编写用于对象之间比较的方法接口
public interface Competitor<T extends Competitor<T>> {Outcom compete(T competitor);}
3.编写具体实现
public enum RoShamBo2 implements Competitor<RoShamBo2> {纸(Outcom.平局, Outcom.失败, Outcom.胜利),剪刀(Outcom.胜利, Outcom.平局, Outcom.失败),石头(Outcom.失败, Outcom.胜利, Outcom.平局);private Outcom o1, o2, o3;RoShamBo2(Outcom paper, Outcom o2, Outcom o3) {o1 = paper;this.o2 = o2;this.o3 = o3;}@Overridepublic Outcom compete(RoShamBo2 competitor) {switch (competitor) {default:case 纸:return o1;case 剪刀:return o2;case 石头:return o3;}}RoShamBo.play(RoShamBo2.class, 20);}}
在上述程序中,通过enum实现了多路分发。
1.1通过构造器为每个实例添加结果信息
public enum RoShamBo2 implements Competitor<RoShamBo2> {纸(Outcom.平局, Outcom.失败, Outcom.胜利),剪刀(Outcom.胜利, Outcom.平局, Outcom.失败),石头(Outcom.失败, Outcom.胜利, Outcom.平局);private Outcom o1, o2, o3;RoShamBo2(Outcom paper, Outcom o2, Outcom o3) {o1 = paper;this.o2 = o2;this.o3 = o3;}
1.1通过enum实现接口中的compete方法、用于两个enum实例进行比较。并返回结果
public Outcom compete(RoShamBo2 competitor) {switch (competitor) {default:case 纸:return o1;case 剪刀:return o2;case 石头:return o3;}
1.3编写RoShamBo类来进行随机产实例和进行分发
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));}}
第一个enum实例将调用competer方法,并且将第二个enum实例参数与case想比较同时。由于每个实例都有comperter方法所以只需要在构造器赋值的时候赋予不同的值即可。
三、使用EnumMap进行分发
使用EnumMap能够实现真正的两路分发
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));}}public interface Competitor<T extends Competitor<T>> {Outcome compete(T competitor);}public class Enums {private static Random rand = new Random(47);public static <T extends Enum<T>> T random(Class<T> ec) {return random(ec.getEnumConstants());}public static <T> T random(T[] values) {return values[rand.nextInt(values.length)];}}
enum RoShamBo5 implements Competitor<RoShamBo5> {PAPER, SCISSORS, ROCK;static EnumMap<RoShamBo5,EnumMap<RoShamBo5,Outcome>>table = new EnumMap<RoShamBo5,EnumMap<RoShamBo5,Outcome>>(RoShamBo5.class);static {for(RoShamBo5 it : RoShamBo5.values())table.put(it,new EnumMap<RoShamBo5,Outcome>(RoShamBo5.class));initRow(PAPER, DRAW, LOSE, WIN);initRow(SCISSORS, WIN, DRAW, LOSE);initRow(ROCK, LOSE, WIN, DRAW);}static void initRow(RoShamBo5 it,Outcome vPAPER, Outcome vSCISSORS, Outcome vROCK) {EnumMap<RoShamBo5,Outcome> row =RoShamBo5.table.get(it);row.put(RoShamBo5.PAPER, vPAPER);row.put(RoShamBo5.SCISSORS, vSCISSORS);row.put(RoShamBo5.ROCK, vROCK);}public Outcome compete(RoShamBo5 it) {return table.get(this).get(it);}public static void main(String[] args) {RoShamBo.play(RoShamBo5.class, 20);}}
