Lombok 和 Spock:@RequiredArgsConstructor 不隐藏具有接口类型的字段的默认无参数构造函数
Lombok and Spock: @RequiredArgsConstructor doesn't hide default no-args constructor for a field with a type of interface
@RequiredArgsConstructor 似乎在下面的代码中不起作用
- 但仅在使用 Spock 框架的测试中,并且仅适用于接口类型 Dao
.
的字段
严格来说 - 代码可以工作,但在我看来它不应该工作,考虑到 JUnit5 下的类似测试根本无法编译。
有人可以解释这是错误还是功能?
package brumba;
public interface Dao {
Integer getValueFor(Integer value);
}
package brumba;
import com.sun.istack.internal.NotNull;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class Brumba {
@NotNull
final private Dao dao;
// If you uncomment the below 2 lines, then the test fails
// @NotNull
// final private String name;
public Integer twice(Integer x){
return x * 2;
}
public Integer twiceDao(Integer x){
return dao.getValueFor(x);
}
}
下面的代码工作正常 - 但仅在 Spock 中(JUnit5 下的类似测试无法编译)。
似乎 Spock 测试以某种方式看到了默认的无参数构造函数(而 JUnit 测试没有'看到这个构造函数)
但是当上面的 2 条注释行被取消注释时,测试失败并出现以下错误:
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: brumba.Brumba()
package brumba
import spock.lang.Specification
class BrumbaTest extends Specification {
def "twice should multiply argument by 2"() {
given:
def testedObject = new Brumba();
expect:
y == testedObject.twice( x )
where:
x | y
0 | 0
1 | 2
2 | 4
3 | 6
}
}
并且此 JUnit 测试根本无法编译:
package brumba;
class BrumbaJUnit5Test {
@org.junit.jupiter.api.Test
void shouldTwice() {
Brumba br = new Brumba();
}
}
错误是:
Error:(7, 21) java: constructor Brumba in class brumba.Brumba cannot be applied to given types;
required: brumba.Dao,java.lang.String
found: no arguments
以下是我在这个项目中使用的依赖项:
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.2-groovy-2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.0-M1</version>
<scope>test</scope>
</dependency>
首先,我可以确认这也发生在我身上。以前没注意过
我不得不通过源代码调试并查看反编译文件,以便至少了解这里发生了什么。我可以告诉你一些事情:
- 这 与 Lombok 无关。 它也发生在任何 Java class 具有采用对象类型的单参数构造函数(即不是像
int
这样的原始类型),例如String
或您的 Dao
.
- 它与 Spock 无关 因为它也发生在 Spock 之外。
- 它似乎 与动态 Groovy 运行时功能有关。
- 我宁愿称其为细微错误而不是功能,但我不确定。
Java class:
package de.scrum_master.Whosebug;
public class Brumba {
public Brumba(String name) {}
}
Groovy class:
package de.scrum_master.Whosebug
class BrumbaApp {
static void main(String[] args) {
new Brumba()
}
}
反编译Groovyclass:
package de.scrum_master.Whosebug;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class BrumbaApp implements GroovyObject {
public BrumbaApp() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].callConstructor(Brumba.class);
}
}
Groovy运行时classCallSite
其实是一个接口,但是有AbstractCallSite
实现了。如果我们看一下这个方法
public Object callConstructor(Object receiver) throws Throwable {
return callConstructor(receiver, CallSiteArray.NOPARAM);
}
和这个定义
public final class CallSiteArray {
// ...
public static final Object [] NOPARAM = new Object[0];
// ...
我们明白实际上这个方法会被调用
public Object callConstructor(Object receiver, Object[] args) throws Throwable {
return CallSiteArray.defaultCallConstructor(this, receiver, args);
}
等等。我认为发生的情况是大小为 0 的 Object[]
将以某种方式作为构造函数参数传递,并且缺少元素被解释为 null
参数。这也是您在对象实例化后在调试器中看到的,如果在您的代码中将参数分配给成员:成员将具有值 null
.
@RequiredArgsConstructor 似乎在下面的代码中不起作用
- 但仅在使用 Spock 框架的测试中,并且仅适用于接口类型 Dao
.
的字段
严格来说 - 代码可以工作,但在我看来它不应该工作,考虑到 JUnit5 下的类似测试根本无法编译。
有人可以解释这是错误还是功能?
package brumba;
public interface Dao {
Integer getValueFor(Integer value);
}
package brumba;
import com.sun.istack.internal.NotNull;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public class Brumba {
@NotNull
final private Dao dao;
// If you uncomment the below 2 lines, then the test fails
// @NotNull
// final private String name;
public Integer twice(Integer x){
return x * 2;
}
public Integer twiceDao(Integer x){
return dao.getValueFor(x);
}
}
下面的代码工作正常 - 但仅在 Spock 中(JUnit5 下的类似测试无法编译)。
似乎 Spock 测试以某种方式看到了默认的无参数构造函数(而 JUnit 测试没有'看到这个构造函数)
但是当上面的 2 条注释行被取消注释时,测试失败并出现以下错误:
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: brumba.Brumba()
package brumba
import spock.lang.Specification
class BrumbaTest extends Specification {
def "twice should multiply argument by 2"() {
given:
def testedObject = new Brumba();
expect:
y == testedObject.twice( x )
where:
x | y
0 | 0
1 | 2
2 | 4
3 | 6
}
}
并且此 JUnit 测试根本无法编译:
package brumba;
class BrumbaJUnit5Test {
@org.junit.jupiter.api.Test
void shouldTwice() {
Brumba br = new Brumba();
}
}
错误是:
Error:(7, 21) java: constructor Brumba in class brumba.Brumba cannot be applied to given types;
required: brumba.Dao,java.lang.String
found: no arguments
以下是我在这个项目中使用的依赖项:
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.2-groovy-2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.0-M1</version>
<scope>test</scope>
</dependency>
首先,我可以确认这也发生在我身上。以前没注意过
我不得不通过源代码调试并查看反编译文件,以便至少了解这里发生了什么。我可以告诉你一些事情:
- 这 与 Lombok 无关。 它也发生在任何 Java class 具有采用对象类型的单参数构造函数(即不是像
int
这样的原始类型),例如String
或您的Dao
. - 它与 Spock 无关 因为它也发生在 Spock 之外。
- 它似乎 与动态 Groovy 运行时功能有关。
- 我宁愿称其为细微错误而不是功能,但我不确定。
Java class:
package de.scrum_master.Whosebug;
public class Brumba {
public Brumba(String name) {}
}
Groovy class:
package de.scrum_master.Whosebug
class BrumbaApp {
static void main(String[] args) {
new Brumba()
}
}
反编译Groovyclass:
package de.scrum_master.Whosebug;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class BrumbaApp implements GroovyObject {
public BrumbaApp() {
CallSite[] var1 = $getCallSiteArray();
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
}
public static void main(String... args) {
CallSite[] var1 = $getCallSiteArray();
var1[0].callConstructor(Brumba.class);
}
}
Groovy运行时classCallSite
其实是一个接口,但是有AbstractCallSite
实现了。如果我们看一下这个方法
public Object callConstructor(Object receiver) throws Throwable {
return callConstructor(receiver, CallSiteArray.NOPARAM);
}
和这个定义
public final class CallSiteArray {
// ...
public static final Object [] NOPARAM = new Object[0];
// ...
我们明白实际上这个方法会被调用
public Object callConstructor(Object receiver, Object[] args) throws Throwable {
return CallSiteArray.defaultCallConstructor(this, receiver, args);
}
等等。我认为发生的情况是大小为 0 的 Object[]
将以某种方式作为构造函数参数传递,并且缺少元素被解释为 null
参数。这也是您在对象实例化后在调试器中看到的,如果在您的代码中将参数分配给成员:成员将具有值 null
.