Mixin 指由已有的类功能组合来组装新的类。

多继承就是Mixin。虽然Java class 是不支持多继承的,但是可以通过 interface 的多继承来组装功能,然后把新的对象里的每一个方法代理给相应的实现该接口的对象就可以。

以下例子是用 植物食物 来组装 蔬菜

PlantFood 接口的定义:

1
2
3
4
5
6
7
8
9
10
11
interface Plant {
String getColor();

void setColor(String val);
}

interface Food {
String getTaste();

void setTaste(String val);
}

PlantFood 接口的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class FoodImp implements Food {
private String taste;

@Override
public String getTaste() {
return taste;
}

@Override
public void setTaste(String val) {
taste = val;
}

}

class PlantImpl implements Plant {
private String color;

@Override
public String getColor() {
return color;
}

@Override
public void setColor(String val) {
color = val;
}
}

有了上面基础类型以后,可以通过动态代理组装一个新的对象,让对象拥有 PlantFood 的功能。

MixinProxy 会记录每一个方法对应的代理者。当具体方法被调用时,找到记录里的代理者让它执行方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Pair<T, U> {
private T left;
private U right;

public Pair(T left, U right) {
this.left = left;
this.right = right;
}

public T getLeft() {
return left;
}
public U getRight() {
return right;
}

public static <T, U> Pair<T, U> of(T left, U right) {
return new Pair<T, U>(left, right);
}
}

class MixinProxy implements InvocationHandler {

private final Map<String, Object> delegates;
public MixinProxy(Pair<?, Class<?>> ...pairs) {
delegates = new HashMap<>();
for (Pair<?, Class<?>> pair: pairs) {
for (Method method: pair.getRight().getMethods()) {
String methodName = method.getName();
if (!delegates.containsKey(methodName)) {
delegates.put(methodName, pair.getLeft());
}
}
}
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object delegate = delegates.get(method.getName());
return method.invoke(delegate, args);
}

public static Object newInstance(Pair<?, Class<?>> ...pairs) {
Set<Class<?>> interfaces = Arrays.stream(pairs).map(pair -> pair.getRight()).collect(Collectors.toSet());
return Proxy.newProxyInstance(pairs[0].getLeft().getClass().getClassLoader(), interfaces.toArray(new Class<?>[pairs.length]), new MixinProxy(pairs));
}
}

给 MixinProxy.newInstance 提供 接口实现实例 和对应的 接口 来实例出组装对象 (vegetable)。组装对象拥有 FoodPlant 的功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Test {
public static void main(String[] args) {
Object vegetable = MixinProxy.newInstance(
Pair.of(new FoodImp(), Food.class),
Pair.of(new PlantImpl(), Plant.class)
);

Food food = (Food) vegetable;
Plant plant = (Plant) vegetable;

food.setTaste("good");
plant.setColor("green");

System.out.println("Taste is " + food.getTaste());
System.out.println("Color is " + plant.getColor());
}
}

程序输出

1
2
Taste is good
Color is green

参考资料

  1. Bruce Eckel, Thinking in Java 4th edition, Mixins