我同意,它们似乎都允许重用多个“类”行为。不过还是有区别的
了解这些可能会帮助您做出决定。
在提供每个功能的简要总结/亮点和合适的示例之前
用法,我们就各自总结一下吧。
结论/典型用法:
-
@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 中不可见。
- 您可以将多个
@Delegates 添加到一个类中。
- 具有委托的类(上例中的
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 中不可见。
混入另一个类的类不是该其他类的实例
您可以将多个班级混入另一个班级
在 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 的处理是明确定义的。
- 典型用法:
- 为不同的类添加相似的特征。
- 从几个特征组成一个新类。