首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >有没有办法用比这更少的代码来扩展现有的接口实现?

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

Stack Overflow用户
提问于 2016-11-16 21:40:28
回答 2查看 92关注 0票数 0

假设我们有一个接口"I“和名为"C1”的"I“到"Cn”的n个实现。让我们进一步假设接口以及实现驻留在一个不能更改的外部库中。最后,让我们假设我们使用的是Java 8。

现在我们想要扩展"I“的所有实现的功能。

如果"I“是可变的,我们可以简单地向它添加具有默认实现的新方法,就像Oracle推出流API一样。不幸的是,"I“是不能改变的。

我目前的解决方案如下:

创建一个新接口"IX“,它扩展了"I”,并包含了所需的新方法作为其默认实现。

因为"C1“到"Cn”没有实现"IX“而是"I",所以我们需要实现"CX1”到"CXn“来利用新功能。这是我不喜欢的部分。我更喜欢一种像匿名类一样即时创建实现的方法。

下面是一些代码示例来说明我的方法:

代码语言:javascript
复制
// 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”的定义根本不需要任何主体实现,所以我认为这些样板文件并不需要它们。实际上,我更喜欢下面这样的内容。

代码语言:javascript
复制
IX object = new (C9 implements IX)() {};

当然,这不是有效的Java代码。这个想法是创建一个匿名类,它基于实现一个附加接口("IX")的现有类("C9")。有没有办法用标准Java做到这一点?

我绝对想用标准Java来做这件事,而不需要字节码操作。当然,我可以使用包装器或动态代理,但是"I“和”C“定义了很多方法。因此,我最终会得到更多的样板代码。

我承认这个问题纯粹是理论上的,因为最终它是关于每个"I“的实现只去掉一行代码。但我仍然对可能的解决方案感兴趣。

如果你喜欢一种更实用的方法,假设你想把流API之类的东西强加给一组未知的实现,而不能改变底层接口。

感谢您对此的意见。

EN

回答 2

Stack Overflow用户

发布于 2016-11-17 01:42:16

解决这类问题的标准方法是委托。由于我们对您的IC1..C9类型一无所知,因此我将使用一个众所周知的接口进行演示:

代码语言:javascript
复制
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();
    }
}

委托实现必须比一个可能为空主体的扩展类做更多的工作,但它只需做一次,并且将与所有接口实现一起工作,甚至不知道它们。

代码语言:javascript
复制
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
EN

Stack Overflow用户

发布于 2016-11-16 22:01:17

如果您没有试图“混合”IX接口,那么一个具有空体的匿名类就可以完成这项工作。

代码语言:javascript
复制
IX object = new C9(){};

(假设C9已经实现了IX)

现在,objectC9的匿名子类的一个实例。

但是,如果C9还没有实现C9,那么除了修改C9之外,就没有其他的解决方案了……这使得C9X变得不必要。

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

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40633631

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档