Groovy 中@Delegate、@Mixin 和 Traits 之间的区别?
Posted
技术标签:
【中文标题】Groovy 中@Delegate、@Mixin 和 Traits 之间的区别?【英文标题】:Difference between @Delegate, @Mixin and Traits in Groovy? 【发布时间】:2014-06-01 01:27:27 【问题描述】:有人能解释一下我什么时候想使用Groovy Traits vs. Mixins (@Mixin) vs. Delegates (@Delegate)?也许一些权衡和设计问题会有所帮助。
它们似乎都允许重用多个“类”行为。谢谢。 :-)
这个 SO 线程也很有帮助:Difference between @Delegate and @Mixin AST transformations in Groovy
【问题讨论】:
【参考方案1】:我同意,它们似乎都允许重用多个“类”行为。不过还是有区别的 了解这些可能会帮助您做出决定。
在提供每个功能的简要总结/亮点和合适的示例之前 用法,我们就各自总结一下吧。
结论/典型用法:
@Delegate:用于添加委托类的所有功能,但仍避免与 实际执行。让你实现composition over inheritance。 @Mixin:在 groovy 2.3 中已弃用。将一个或多个类中的方法添加到您的类中的简单方法。漏洞百出。 Runtime mixin:将一个或多个方法添加到 any 现有类中,例如JDK 或第 3 方库中的类。 特征:groovy 2.3 中的新特性。向您的类添加一个或多个特征的明确定义的方法。替换@Mixin。唯一的 在 Java 类中可以看到添加的方法之一。现在,让我们更详细地研究其中的每一个。
@Delegate
在许多情况下,继承被过度使用。也就是说,它经常被不当使用。 Java中的经典示例是
扩展输入流、读取器或集合类。对于其中大多数,使用继承也是
与实施紧密结合。也就是说,实际的实现是这样编写的,以便
公共方法实际上使用了另一个。如果您同时覆盖两者,并致电super
,那么您可能会不受欢迎
副作用。如果在以后的版本中实现发生变化,那么您将不得不更新您的处理
也是。
相反,您应该努力使用composition over 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)
,所以它不会那么直截了当
使用继承来实现。
总结:
为委托对象中的所有公共方法提供默认实现。 显式添加的具有相同签名的方法优先。 隐式添加的方法在 Java 中不可见。 您可以将多个@Delegate
s 添加到一个类中。
但如果您这样做,您应该考虑这是否真的可取。
diamond problem 怎么样,即如果您在委托中有多个具有相同签名的方法?
具有委托的类(上例中的CountingList
)不是委托类的实例。
即CountingList
不是 LinkedList
的实例。
用于通过继承避免紧密耦合。
@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 不要添加到“超级”类(至少,我遇到过问题) 漏洞百出 从 groovy 2.3 中弃用 隐式添加的方法在 Java 中不可见。 混入另一个类的类不是该其他类的实例 即Second
不是 First
的实例
您可以将多个班级混入另一个班级
diamond problem 怎么样,即,如果您在混合类中具有相同签名的方法?
在 groovy 中用作将一个类的功能添加到另一个类的简单方法
运行时混合
运行时 mixins 和 @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 还有一个不错的 extension module 功能,您无需手动执行 mixin,而是 只要在类路径中的正确位置找到模块描述符,groovy 就会为您执行此操作。
总结:
向任何现有类添加方法 JDK 中的任何类 任何第 3 方课程 或您自己的任何课程 覆盖具有相同签名的任何现有方法 添加的方法在 Java 中不可见 通常用于使用新功能扩展现有/第 3 方类特征
特征是 groovy 2.3 的新特性。
我倾向于将这些特征视为熟悉的界面和类之间的某种东西。类似于“轻量级”的东西 班级。它们在文档中被称为“具有默认实现和状态的接口”。
特征类似于它们替换的@Mixin
转换,但它们也更强大。首先,他们
更加明确。 trait 不能直接实例化,就像接口一样,它们需要实现
班级。一个类可以实现许多特征。
一个简单的例子:
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 转换。
此外,它可以包含需要由类实现的抽象方法。此外,一个类可以实现
几个特点。实现 trait 的类是该 trait 的一个实例。
总结:
特征提供具有实现和状态的接口。 一个类可以实现多个特征。 特征实现的方法在 Java 中是可见的。 兼容类型检查和静态编译。 特征可以实现接口。 特征不能自行实例化。 一个特征可以扩展另一个特征。 diamond problem 的处理是明确定义的。 典型用法: 为不同的类添加相似的特征。 (作为 AOP 的替代品) 从几个特征组成一个新类。【讨论】:
您的回答很好,但请注意,这些特征与 @CompileStatic 等 AST 转换不正式兼容。 beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/… 非常感谢这个解释清楚的答案!你推荐什么关于 Groovy 的书? @AlexanderSuraphel 以我的经验,Groovy in Action 是一本很棒的书。它不仅深入地展示了 Groovy,而且还很好地讨论了一般编程概念以及 Groovy 如何帮助而不是替代 Java,以提供极其强大的开发环境。 Steinar,你提到的“钻石问题”是如何处理的? @AlexanderSuraphel:我猜迟到总比没有好,请参阅this answer,了解如何处理钻石问题。以上是关于Groovy 中@Delegate、@Mixin 和 Traits 之间的区别?的主要内容,如果未能解决你的问题,请参考以下文章
Groovy闭包 Closure ( 闭包的 delegate 代理策略 | OWNER_FIRST | DELEGATE_FIRST | OWNER_ONLY | DELEGATE_ONLY )
错误记录Groovy 闭包使用报错 ( 闭包中不能直接使用外部对象的方法 | 需要先设置 delegate 代理 )
为了让 groovy 闭包修改在委托范围内定义的变量,是不是需要显式指定 delegate.theVariableName?
GroovyMOP 元对象协议与元编程 ( 方法委托 | 使用 @Delegate 注解进行方法委托 )
Groovy闭包 Closure ( 闭包类 Closure 简介 | thisownerdelegate 成员赋值及源码分析 )