有没有办法用比这更少的代码扩展现有的接口实现?

时间:2016-11-16 13:40:28

标签: java java-8

我们假设我们有一个界面"我"和" 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的内容,而无法更改底层接口。

感谢您对此的看法。

2 个答案:

答案 0 :(得分:1)

解决此类事情的标准方法是委托。由于我们对您的IC1 .. 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())));

这类似于checkedSetsynchronizedSetunmodifiableSet增强(或限制)现有Set实施的方式。

答案 1 :(得分:0)

如果你没有尝试&#34; mixin&#34; IX接口,一个空主体的匿名类可以完成这项任务。

IX object = new C9(){};

(这假设C9已实施IX

现在objectC9的匿名子类的实例。

但如果C9尚未实现C9,除了修改 C9之外,没有纯Java解决方案......这会呈现{ {1}}不必要。

但请注意,修改C9X以实现C9应该是良性的。首先,它不会破坏二进制兼容性。