@Delegate,@Mixin和Groovy中的特性之间的区别?

有人会解释什么时候我想要使用Groovy Traits vs. Mixins(@Mixin)还是Delegates(@Delegate)? 也许一些权衡和devise问题会有所帮助。

他们似乎都允许重复使用多个“类”的行为。 谢谢。 🙂

这个SO线程也很有帮助: Groovy中的@Delegate和@Mixin AST转换之间的区别

我同意,他们似乎都允许重复使用多个“类”的行为。 虽然有分歧,理解这些可能会有助于你的决定。

在提供每个特征的简要总结/突出以及合适用法的例子之前,我们只对每个的结论进行总结。

结论/典型用法:

  • @Delegate :用于添加委托类的所有function,但仍然避免与实际的实现紧密耦合。 让我们来实现inheritance的组合 。
  • @Mixin不赞成使用Groovy 2.3。 将一个或多个类的方法添加到您的类中的简单方法。 充满错误的。
  • 运行时混入 :将一个或多个方法添加到任何现有的类中,例如JDK中的类或第三方库。
  • 特点 :新的常规2.3。 定义明确的方式来添加一个或多个特征到你的class级。 取代@Mixin。 在Java类中可以看到添加方法的唯一方法。

现在我们来看看每一个细节。

@代表

inheritance在许多情况下被过度使用。 也就是说,它经常被不恰当的使用。 Java中的经典示例是扩展inputstream,读取器或集合类。对于其中大部分,使用inheritance与实现紧密耦合。 也就是说,实际的实现是这样编写的,以便其中一个公共方法实际上使用另一个。 如果你重写这两个,而你打电话super ,那么你可能会得到不需要的副作用。 如果实现在更高版本中更改,那么您将不得不更新对它的处理。

相反,你应该努力使用构造而不是inheritance 。

例如,计数添加到列表中的元素的计数列表:

 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) ,所以用inheritance来实现并不是那么简单。

概要:

  • 为委托对象中的所有公共方法提供默认实现。
    • 具有相同签名且明确添加的方法优先。
  • 隐式添加的方法在Java中可见。
  • 你可以添加几个@Delegate s到一个类。
    • 但如果你这样做,你应该考虑这是否真的是可取的。
    • 那么钻石问题呢 ,也就是说,如果你在代表签名中有多种方法?
  • 带有委托的类(上例中的CountingList )不是委托类的实例。
    • CountingList不是LinkedList一个实例。
  • 用于避免通过inheritance紧密耦合。

@Mixin

由于即将到来的特性支持, @Mixin转换将被弃用于groovy 2.3。 这提供了一个暗示,即@Mixin可能做的所有事情都应该可以用特质做。

根据我的经验, @Mixin是一种混合的祝福。 🙂

由核心开发人员承认,错误的是“难以解决”的错误。 这并不是说它是“无用的”,远非如此。 但如果你有机会使用(或等待)groovy 2.3,那么你应该使用特质来代替。

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中用于从一个类到另一个类的简单添加方法
    • 不要添加到“超级”类(至less,我有问题)
  • 充满错误的
  • 从时髦2.3弃用
  • 隐式添加的方法在Java中可见。
  • 获得另一个类的类不是其他类的实例
    • 也就是说Second不是First一个例子
  • 你可以把几个class级混合成一个class级
    • 钻石问题呢 ,也就是说,如果你的方法在混合类中有相同的签名?
  • 用作在groovy <2.3中将一个类的function添加到另一个类的简单方法

运行时混入

运行时mixin和@Mixin转换是完全不同的,它们解决了不同的使用情况,并用于完全不同的情况。 由于他们有相同的名字,很容易混淆彼此,或者认为他们是一个一样的。 运行时混入,但是,在groovy 2.3 被弃用。

我倾向于将运行时mixin作为将方法添加到现有类(例如JDK中的任何类)的方式。 这是Groovy为JDK添加额外方法所使用的机制。

例:

 class MyStringExtension { public static String hello(String self) { return "Hello $self!" } } String.mixin(MyStringExtension) assert "Vahid".hello() == 'Hello Vahid!' 

Groovy也有一个很好的扩展模块function,在这里你不需要手动执行mixin,只要在类path的正确位置find模块描述符就可以了。

概要:

  • 将方法添加到任何现有的类
    • JDK中的任何类
    • 任何第三方课程
    • 或任何你自己的class级
  • 用相同的签名覆盖任何现有的方法
  • Java中添加的方法可见
  • 通常用于扩展具有新function的现有/第三方类

性状

特点是新的常规2.3。

我倾向于将这些特征看作熟悉的界面和类之间的东西。 类似于“轻量级”的类。 它们在文档中被称为“具有默认实现和状态的接口”。

特性类似于他们所取代的@Mixin变换,但是它们也更加强大。 对于初学者来说,他们更加明确。 一个特征不能直接实例化,就像一个接口一样,他们需要一个实现类。 而一个class可以实现很多特质。

一个简单的例子:

 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 

特质和@Mixin之间的直接差异是trait是一种语言关键字,而不是AST变换。 此外,它可以包含需要由类实现的抽象方法。 此外,一个class级可以实现几个特点。 实现特质的类是该特性的一个实例。

概要:

  • 特征提供了一个实现和状态的接口。
  • 一个类可以实现多个特性。
  • 在Java 可以看到由特征实现的方法。
  • 兼容types检查和静态编译。
  • 特质可以实现接口。
  • 性状不能被自己实例化。
  • 特质可以延伸另一个特点。
  • 钻石问题的处理是明确的。
  • 典型用法:
    • 添加类似的特征到不同的类。
      • (作为AOP的替代)
    • 从几个特点组成一个新class级。