假设我们有一个接口"I“和名为"C1”的"I“到"Cn”的n个实现。让我们进一步假设接口以及实现驻留在一个不能更改的外部库中。最后,让我们假设我们使用的是Java 8。
现在我们想要扩展"I“的所有实现的功能。
如果"I“是可变的,我们可以简单地向它添加具有默认实现的新方法,就像Oracle推出流API一样。不幸的是,"I“是不能改变的。
我目前的解决方案如下:
创建一个新接口"IX“,它扩展了"I”,并包含了所需的新方法作为其默认实现。
因为"C1“到"Cn”没有实现"IX“而是"I",所以我们需要实现"CX1”到"CXn“来利用新功能。这是我不喜欢的部分。我更喜欢一种像匿名类一样即时创建实现的方法。
下面是一些代码示例来说明我的方法:
// Instantiate existing "C9" implementation of "I"
I object = new C9();
// Definition of interface "IX"
public interface IX extends I {
default void newMethod() {
// Do something
}
}
// Example definition of implementation "CX9"
// Off course, this needs to be done for all "C1" to "Cn".
public class CX9 extends C9 implements IX {}
// Instantiate extended implementation "C9"
IX object = new CX9();因为"CX1“到"CXn”的定义根本不需要任何主体实现,所以我认为这些样板文件并不需要它们。实际上,我更喜欢下面这样的内容。
IX object = new (C9 implements IX)() {};当然,这不是有效的Java代码。这个想法是创建一个匿名类,它基于实现一个附加接口("IX")的现有类("C9")。有没有办法用标准Java做到这一点?
我绝对想用标准Java来做这件事,而不需要字节码操作。当然,我可以使用包装器或动态代理,但是"I“和”C“定义了很多方法。因此,我最终会得到更多的样板代码。
我承认这个问题纯粹是理论上的,因为最终它是关于每个"I“的实现只去掉一行代码。但我仍然对可能的解决方案感兴趣。
如果你喜欢一种更实用的方法,假设你想把流API之类的东西强加给一组未知的实现,而不能改变底层接口。
感谢您对此的意见。
发布于 2016-11-17 01:42:16
解决这类问题的标准方法是委托。由于我们对您的I和C1..C9类型一无所知,因此我将使用一个众所周知的接口进行演示:
public interface ExtSet<T> extends Set<T> {
default T reduce(BinaryOperator<T> op) {
Iterator<T> it = iterator();
T t=it.next();
while(it.hasNext()) t=op.apply(t, it.next());
return t;
}
public static <T> ExtSet<T> enhance(Set<T> set) {
if(set instanceof ExtSet) return (ExtSet<T>)set;
final class Enhanced extends AbstractSet<T> implements ExtSet<T> {
public Iterator<T> iterator() { return set.iterator(); }
public int size() { return set.size(); }
public boolean contains(Object o) { return set.contains(o); }
public boolean add(T e) { return set.add(e); }
public boolean remove(Object o) { return set.remove(o); }
}
return new Enhanced();
}
}委托实现必须比一个可能为空主体的扩展类做更多的工作,但它只需做一次,并且将与所有接口实现一起工作,甚至不知道它们。
ExtSet<String> set1=ExtSet.enhance(new TreeSet<>());
Collections.addAll(set1, "foo", "bar", "baz");
System.out.println(set1.reduce(String::concat));
ExtSet<Integer> set2=ExtSet.enhance(new HashSet<>());
Collections.addAll(set2, 100, 42, 7);
System.out.println(set2.reduce(Integer::sum));
ExtSet<Thread.State> set3=ExtSet.enhance(
EnumSet.of(Thread.State.TERMINATED, Thread.State.NEW));
System.out.println(set3.reduce(BinaryOperator.minBy(Comparator.naturalOrder())));这类似于checkedSet、synchronizedSet和unmodifiableSet如何增强(或限制)现有的Set实现。
发布于 2016-11-16 22:01:17
如果您没有试图“混合”IX接口,那么一个具有空体的匿名类就可以完成这项工作。
IX object = new C9(){};(假设C9已经实现了IX)
现在,object是C9的匿名子类的一个实例。
但是,如果C9还没有实现C9,那么除了修改C9之外,就没有其他的解决方案了……这使得C9X变得不必要。
但请注意,修改C9以实现IX应该是良性的。首先,它不会破坏二进制兼容性。
https://stackoverflow.com/questions/40633631
复制相似问题