Java 中的 traits 或 mixins 模式?

21

有没有办法在Java中模拟mixins或traits?基本上,我需要一种方法来进行多重继承,以便我可以向多个类添加共同的业务逻辑。

8个回答

18

这不是你想要的方式。Effective Java建议你“优先使用组合而非继承”。这意味着你将通用逻辑移动到其他类并委托处理。这是你解决Java中缺乏多重继承的方法。


13

我会将所有业务逻辑封装到一个名为BusinessLogic的新类中,并让需要使用BusinessLogic的每个类调用该类。如果你需要一个根类层次结构,使得调用BusinessLogic的类只有一个根类,那么你还需要创建一个接口(BusinessLogicInterface?)。

伪代码如下:

interface BusinessLogicInterace
{
    void method1();
    void method2();
}

class BusinessLogic implements BusinessLogicInterface
{
    void method1() { ... }
    void method2() { ... }
}

class User 
    extends OtherClass 
    implements BusinessLogicInterface
{
    BusinessLogic logic = new BusinessLogic();

    @Override
    void method1() { logic.method1(); }

    @Override
    void method2() { logic.method2(); }
}

这并不是最漂亮的实现方式,用来解决多重继承的缺失,在接口有很多方法时会变得非常麻烦。 很可能,你希望尝试重新设计你的代码,避免需要使用混合。


1
好的,那么有没有什么技巧可以避免使用委托和显式调用logic.method1、logic.method2、logic.method3的逻辑呢?这将有助于节省打字和样板代码。 - joshjdevl
1
@joshjdevl 有,但这意味着篡改编译器 - maaartinus
Lombok的@Delegate:https://projectlombok.org/features/Delegate.html - Marcin Orlowski

5

今天您是否想要发挥纯粹的对象思维?

您是否认为需要一些复合导向编程?

如果是这样,那么您需要了解Apache Polygene(曾用名Qi4J,后改名为Zest和/或Apache-Zest)。

更新于2022年:虽然它已经停止更新,但仍然很有用。


它支持特质还是只支持混入?即,当两个具有相同签名的方法发生冲突时,是一个方法胜出,还是会显示编译器错误并提供一些解决冲突的方法? - Neil Traft
@NeilTraft 方法解析是可预测的,并且基于声明顺序,详见http://qi4j.org/_core.html#core-api-mixin - eskatos

4
你可以利用接口允许嵌套类(自动公共静态)的事实,将接口方法的默认实现封装在接口本身中。即将Alex B示例中的BusinessLogic类移至接口内部。
这类似于Scala为特质生成JVM代码的方式,如此处所述How are Scala traits compiled into Java bytecode? 这样做后,示例变为:
interface BusinessLogicInterface {
    void method0();

    class DefaultImpl {
        private DefaultImpl() {
        }

        public static void method1(BusinessLogicInterface self) { ... }
        public static void method2(BusinessLogicInterface self) { ... }
    }

    void method1();
    void method2();
}

class User extends OtherClass implements BusinessLogicInterface {
    @Override
    void method0() { ... }

    @Override
    void method1() { BusinessLogic.defaultImpl.method1(this); }

    @Override
    void method2() { BusinessLogic.defaultImpl.method2(this); }
}

请注意,我们将接口类型的对象作为“self”参数传递。这意味着业务逻辑可以使用其他抽象方法(method0)。这对于创建具有相互正交的抽象方法和可能基于这些正交方法实现的实用程序“扩展”方法的特质非常有用。
缺点是每个接口必须复制/粘贴模板委托代码。另一个经常在Java中使用的模式(但具有较少的内聚性和较少的面向对象方式调用方法)是创建一个以复数名称作为包含静态方法的接口,这在Collections实用程序类中使用。

1
请注意,Java 8将包含“虚拟扩展方法”,这将使得更加容易。 - Henno Vermeulen

3

QI4J 允许您使用混合功能


3

Java的解决多重继承的方法是实现多个接口。当然,这意味着您将获得方法声明,但不包括逻辑。

您可以通过组合来模拟mixin:您的Java类可以定义表示执行某些通用业务逻辑的其他类的成员变量。

在设计Java类时,我并没有发现缺乏C++风格的多重继承会抑制我的架构设计。您会找到一种实现您想要的功能的方法。


2

从Java-8开始,添加了默认接口方法。这与Java中的多继承接口一起使用应该允许某种混合。显然,这些接口必须独立操作。因此,会有显著的限制。


0

在Java中使用CGLib/javassit实现简单的mixin/traits支持非常容易。 例如,您可以查看此处的小例子。 更完整、可直接使用的解决方案可以在此处找到。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接