在Groovy中,@Delegate、@Mixin和Traits有什么区别?

58
1个回答

121
我同意,它们似乎都允许重用多个“类”行为。不过,它们存在差异,了解这些差异可能有助于你做出决策。
在提供每个功能的简要总结/重点和适当使用示例之前,让我们先总结一下每个功能的结论/典型用法。
结论/典型用法:
- @Delegate: 用于添加委托类的所有功能,但仍避免与实际实现紧密耦合。让你实现组合优于继承
- @Mixin: 在groovy 2.3中已弃用。简单地将一个或多个类的方法添加到你的类中。存在缺陷。
- 运行时mixin: 将一个或多个方法添加到任何现有类中,例如JDK中的类或第三方库中的类。
- Traits: 在groovy 2.3中新增。一种明确定义的方法,用于向你的类添加一个或多个traits。替代@Mixin。这是唯一一个添加的方法可见于Java类中的功能。
现在,让我们更详细地了解每个功能。

@Delegate

继承在很多情况下被过度使用,也就是说经常被错误地使用。Java中的典型例子是扩展输入流、读取器或集合类。对于这些大多数情况,使用继承与实现过于紧密耦合。也就是说,实际实现被编写成其中一个公共方法实际上使用另一个公共方法。如果你同时重写它们并调用super,那么你可能会得到意想不到的副作用。如果实现在以后的版本中发生更改,则还必须更新相应的处理。

相反,你应该努力使用组合而非继承

例如,一个计数列表,它记录添加到列表中的元素数量:

class CountingList<E> {
    int counter = 0
    @Delegate LinkedList<E> list = new LinkedList<>()
    boolean addAll(Collection<? extends E> c) {
        counter += c.size()
        list.addAll(c)
    }
    boolean addAll(int index, Collection<? extends E> c) {
        counter += c.size()
        list.addAll(index, c)
    }
    // more add methods with counter updates
}

在这个例子中,@Delegate 可以消除所有公共方法的样板代码,使你可以保留那些想要“原样保留”的方法,即添加一些简单的调用底层列表的方法。此外,CountingList 与实现分离,因此您不必关心其中一个方法是通过调用另一个方法来实现的。在上面的例子中,实际上就是这种情况,因为LinkedList.add(Collection) 调用了 LinkedList.add(int, Collection),所以使用继承实现并不是那么直观。
总结:
  • 为委托对象的所有公共方法提供默认实现。
  • 具有相同签名的显式添加的方法优先。
  • Java中不可见隐式添加的方法。
  • 您可以向一个类添加多个@Delegate,但如果这样做,您应该考虑是否真的需要。
  • 当代理中有多个具有相同签名的方法时,如diamond problem,该怎么办?
  • 具有代理的类(上面示例中的CountingList)不是代理类的实例。
  • 用于避免通过继承紧密耦合。

@Mixin

由于即将到来的特征支持,@Mixin转换将在groovy 2.3中被弃用。这提供了一种暗示,即使用特征应该能够完成使用@Mixin所能完成的一切。

在我的经验中,@Mixin 有点双刃剑。:) 核心开发人员承认它存在着“难以解决”的错误。这并不是说它是“无用的”,远非如此。但如果你有机会使用(或等待)Groovy 2.3,则应该使用traits代替。AST转换所做的就是将一个类的方法添加到另一个类中。例如:
class First {
    String hello(String name) { "Hello $name!" }
}

@Mixin(First)
class Second {
    // more methods
}

assert new Second().hello('Vahid') == 'Hello Vahid!'

简介:

  • 将一个类的方法添加到另一个类中。
  • 在groovy <2.3中使用,用于将一个类的方法简单地添加到另一个类中
    • 不要添加到“super”类中(至少我遇到了这个问题)
  • 有许多错误
  • 从groovy 2.3开始已弃用
  • 隐式添加的方法在Java中是不可见的。
  • 获得其他类混合的类不是该其他类的实例
    • 例如,Second不是First的实例
  • 您可以将多个类混合到另一个类中
    • 那么diamond problem怎么办?即,如果混入的类中有相同签名的方法?
  • 在groovy <2.3中,将一个类的功能简单地添加到另一个类中的一种简单方法

运行时混入

运行时混入和@Mixin转换是完全不同的,它们解决不同的用例并在完全不同的情况下使用。由于它们具有相同的名称,很容易将一个与另一个混淆,或者认为它们是一样的。然而,在Groovy 2.3中,运行时混合并没有被弃用。
我倾向于将运行时混入视为向现有类添加方法的方式,例如JDK中的任何类。这是Groovy用于向JDK添加额外方法的机制。
例如:
class MyStringExtension {
    public static String hello(String self) {
        return "Hello $self!"
    }
}

String.mixin(MyStringExtension)

assert "Vahid".hello() == 'Hello Vahid!'

Groovy还有一个不错的扩展模块功能扩展模块,您不需要手动执行混合操作,只要groovy在类路径中找到正确位置的模块描述符即可为您执行。
总结:
- 向任何现有类添加方法 - JDK中的任何类 - 任何第三方类 - 或者您自己的任何类 - 用相同签名覆盖任何现有方法 - 添加的方法在Java中不可见 - 通常用于扩展现有/第三方类的新功能
特征:
特征是groovy 2.3新增功能。
我倾向于将这些特征视为熟悉接口和类之间的某种东西。类似于“轻量级”类。它们在文档中被称为具有默认实现和状态的“带接口”的实现。
特质与它们替代的@Mixin转换相似,但它们也更强大。首先,它们更加明确定义。类似于接口,特质不能直接实例化,它们需要一个实现类。一个类可以实现多个特质。
一个简单的例子:
trait Name {
    abstract String name()
    String myNameIs() { "My name is ${name()}!" }
}
trait Age {
    int age() { 42 }
}

class Person implements Name, Age {
    String name() { 'Vahid' }
}

def p = new Person()
assert p.myNameIs() == 'My name is Vahid!'
assert p.age() == 42
assert p instanceof Name
assert p instanceof Age

Traits和@Mixin之间的直接区别在于,trait是一个语言关键字,而不是AST转换。此外,它可以包含需要被类实现的抽象方法。此外,一个类可以实现多个traits。实现trait的类是该trait的实例。
总结:
  • Traits提供了带有实现和状态的接口。
  • 一个类可以实现多个traits。
  • 由trait实现的方法在Java中是可见的。
  • 与类型检查和静态编译兼容。
  • Traits可以实现接口。
  • Traits本身不能被实例化。
  • 一个trait可以扩展另一个trait。
  • diamond problem的处理是明确定义的。
  • 典型用法:
    • 将相似的traits添加到不同的类中。
      • (作为AOP的替代方案)
    • 从几个traits组合出一个新类。

1
你的回答很好,但请注意,特质与@CompileStatic等AST转换不是官方兼容的。http://beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/core-traits.html#_compatibility_with_ast_transformations - Iván López
2
非常感谢您提供这个解释得很清楚的答案!您推荐哪本关于Groovy的书籍? - Alexander Suraphel
1
@AlexanderSuraphel 在我的经验中,《Groovy in Action》是一本非常优秀的书籍。它不仅深入展示了 Groovy,还对一般编程概念进行了很好的讨论,并阐述了 Groovy 如何帮助 Java 而不是取代它,从而提供了一个极其强大的开发环境。 - Renato
Steinar,你提到的情况中,“钻石问题”是如何处理的? - Alexander Suraphel
太棒了的回答。谢谢。 - Sean
1
@AlexanderSuraphel:迟做总比不做好,可以查看此答案了解钻石问题的处理方式。 - Steinar

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