从Groovy的早期开始,我们可以创建POGO(Plain Old Groovy Objects)类,它们将具有带有Map
参数的构造函数。 Groovy在生成的类中自动添加构造函数。我们可以使用命名参数来创建POGO的实例,因为Map
参数构造函数。 这只有在我们不添加自己的构造函数且属性不是最终的时才有效。从Groovy 2.5.0开始,我们可以使用@MapConstrutor
AST转换注释来添加带有Map
参数的构造函数。使用注释我们可以有更多选项来自定义生成的构造函数。例如,我们可以让Groovy使用Map
参数生成构造函数,并添加我们自己的构造函数。 属性也可以是final,我们仍然可以使用带有Map
参数的构造函数。
首先,我们在创建POGO时查看Groovy中的默认行为:
// Simple POGO.
// Groovy adds Map argument
// constructor to the class.
class Person {
String name
String alias
List<String> likes
}
// Create Person object using
// the Map argument constructor.
// We can use named arguments,
// with the name of key being
// the property name. Groovy
// converts this to Map.
def mrhaki =
new Person(
alias: 'mrhaki',
name: 'Hubert Klein Ikkink',
likes: ['Groovy', 'Gradle'])
assert mrhaki.alias == 'mrhaki'
assert mrhaki.name == 'Hubert Klein Ikkink'
assert mrhaki.likes == ['Groovy', 'Gradle']
// Sample class with already
// a constructor. Groovy cannot
// create a Map argument constructor now.
class Student {
String name
String alias
Student(String name) {
this.name = name
}
}
import static groovy.test.GroovyAssert.shouldFail
// When we try to use named arguments (turns into a Map)
// in the constructor we get an exception.
def exception = shouldFail(GroovyRuntimeException) {
def student =
new Student(
name: 'Hubert Klein Ikkink',
alias: 'mrhaki')
}
assert exception.message.startsWith('failed to invoke constructor: public Student(java.lang.String) with arguments: []')
assert exception.message.endsWith('reason: java.lang.IllegalArgumentException: wrong number of arguments')
现在让我们在下一个例子中使用@MapConstructor
注释:
import groovy.transform.MapConstructor
@MapConstructor
class Person {
final String name // AST transformation supports read-only properties.
final String alias
List<String> likes
}
// Create object using the Map argument constructor.
def mrhaki =
new Person(
name: 'Hubert Klein Ikkink',
alias: 'mrhaki',
likes: ['Groovy', 'Gradle'])
assert mrhaki.name == 'Hubert Klein Ikkink'
assert mrhaki.alias == 'mrhaki'
assert mrhaki.likes == ['Groovy', 'Gradle']
// Using the annotation the Map argument
// constructor is added, even though we
// have our own constructor as well.
@MapConstructor
class Student {
String name
String alias
Student(String name) {
this.name = name
}
}
def student =
new Student(
name: 'Hubert Klein Ikkink',
alias: 'mrhaki')
assert student.name == 'Hubert Klein Ikkink'
assert student.alias == 'mrhaki'
AST转换支持几个属性。 我们可以使用属性includes
和excludes
来包含或排除将在Map
参数构造函数中获取值的属性。 在下面的例子中,我们看到了如何使用includes
属性:
import groovy.transform.MapConstructor
@MapConstructor(includes = 'name')
class Person {
final String name
final String alias
List<String> likes
}
// Create object using the Map argument constructor.
def mrhaki =
new Person(
name: 'Hubert Klein Ikkink',
alias: 'mrhaki',
likes: ['Groovy', 'Gradle'])
assert mrhaki.name == 'Hubert Klein Ikkink'
assert !mrhaki.alias
assert !mrhaki.likes
我们可以使用属性pre
和post
通过AST转换添加在生成的代码之前或之后执行的自定义代码。 我们使用需要执行的代码为这些属性分配一个Closure
。
在下一个示例中,我们使用代码设置pre
属性,如果未通过构造函数设置,则该代码计算alias
属性值:
// If alias is set in constructor use it, otherwise
// calculate alias value based on name value.
@MapConstructor(post = { alias = alias ?: name.split().collect { it[0] }.join() })
class Person {
final String name // AST transformation supports read-only properties.
final String alias
List<String> likes
}
// Set alias in constructor.
def mrhaki =
new Person(
name: 'Hubert Klein Ikkink',
alias: 'mrhaki',
likes: ['Groovy', 'Gradle'])
assert mrhaki.name == 'Hubert Klein Ikkink'
assert mrhaki.alias == 'mrhaki'
assert mrhaki.likes == ['Groovy', 'Gradle']
// Don't set alias via constructor.
def hubert =
new Person(
name: 'Hubert A. Klein Ikkink')
assert hubert.name == 'Hubert A. Klein Ikkink'
assert hubert.alias == 'HAKI'
assert !hubert.likes
用Groovy 2.5.0编写。