我们假设我们有一个界面"我"和" I"的实现命名" C1"到" Cn"。让我们进一步假设接口以及实现驻留在一个无法更改的外部库中。最后,让我们假设我们使用的是Java 8.
现在我们想要扩展" I"的所有实现的功能。
如果"我"我们可以简单地添加带有默认实现的新方法,就像Oracle推出流式API一样。很遗憾"我"是不可改变的。
我目前的解决方案如下:
创建一个新界面" IX"延伸"我"并包含所需的新方法作为默认实现。
因为" C1"到" Cn"不执行" IX"但是我们需要实施" 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代码。我们的想法是创建一个匿名类,它基于现有的类(" C9")实现一个额外的接口(" IX")。有没有办法用标准Java做到这一点?
我绝对想用标准的Java做这个,没有字节码操作。当然,我可以使用包装或动态代理但是"我"并且" C" s定义了许多方法。因此,我最终会得到更多的样板代码。
我承认这个问题纯粹是学术性的,因为最终它只是在每次实现" I"时只删除一行代码。但我仍然对可能的解决方案感兴趣。
如果您喜欢更实用的方法,假设您希望在未知的实现集上强加类似流API的内容,而无法更改底层接口。
感谢您对此的看法。
答案 0 :(得分:1)
解决此类事情的标准方法是委托。由于我们对您的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
实施的方式。
答案 1 :(得分:0)
如果你没有尝试&#34; mixin&#34; IX
接口,一个空主体的匿名类可以完成这项任务。
IX object = new C9(){};
(这假设C9
已实施IX
)
现在object
是C9
的匿名子类的实例。
但如果C9
尚未实现C9
,除了修改 C9
之外,没有纯Java解决方案......这会呈现{ {1}}不必要。
但请注意,修改C9X
以实现C9
应该是良性的。首先,它不会破坏二进制兼容性。