Public groovy method must be public, says the compiler - groovy

What is the cause of this error, and how do I fix it?
At first glance, it seems like a defect in the groovy compiler.
:compileIntegrationTestGroovystartup failed:
C:\src\my-project\src\integration-test\groovy\my\project\MyServiceISpec.groovy: 31: The method setup should be public as it implements the corresponding method from interface my.project.MyTrait
. At [31:5] # line 31, column 5.
public void setup() {
^
1 error
My grails integration test looks like this:
#Integration
#Rollback
class MyServiceISpec extends Specification implements MyTrait {
#Autowired
MyService service
OtherService otherService = Mock()
public void setup() {
myTraithMethod()
service.otherService = otherService
}
}
My trait looks like this:
trait MyTrait {
public void setup() {
myTraithMethod()
}
private myTraitMethod() {
...
}
}
Update Added public keyword to the trait setup method.

I think that the source of that problem is AST, because Spock uses AST transformations and compiles the specification. You can read here http://docs.groovy-lang.org/next/html/documentation/core-traits.html#_compatibility_with_ast_transformations this:
Traits are not officially compatible with AST transformations. Some of
them, like #CompileStatic will be applied on the trait itself (not on
implementing classes), while others will apply on both the
implementing class and the trait. There is absolutely no guarantee
that an AST transformation will run on a trait as it does on a regular
class, so use it at your own risk!
You can solve it by renaming setup() method in the trait on traitSetup() for example and calling it from the specification setup() method like this:
#Integration
#Rollback
class MyServiceISpec extends Specification implements MyTrait {
#Autowired
MyService service
OtherService otherService = Mock()
void setup() {
service.otherService = otherService
traitSetup()
}
def 'some test here'() {
...
}
}
trait MyTrait {
void traitSetup() {
myTraitMethod()
}
private myTraitMethod() {
...
}
}

1/ Not sure, but the trait name is ResetsDatabase and your test implements MyTrait.
Possibly some mixup with traits ?
2/ It seems to me that you can't have a public method on the implemented method if your traits says the the method (setup here) is private.

Related

How to reference subclass from a static superclass method in Groovy

A simplified version of what I'm trying to do in Groovy:
class Animal {
static def echo() {
println this.name // ie "class.name"
}
}
class Dog extends Animal {
}
class Cat extends Animal {
}
Dog.echo()
Cat.echo()
// Output:
// => Animal
// => Animal
//
// What I want:
// => Dog
// => Cat
I think what I'm asking here is: when I call a static method on an object, and
the static method is defined in the object's superclass, is there a way to obtain
the actual type of the object?
A static method is not defined in the object context, but in the class context. You might get confused by the presence of this in the Groovy static method. However, it's only a syntactic sugar that eventually replaces this.name with Animal.class.name.
If you compile the Animal class from your example with a static compilation enabled, you will see that it compiles to the following Java equivalent (result after decompiling the .class file):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
public class Animal implements GroovyObject {
public Animal() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static Object echo() {
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
return null;
}
}
You can see that the following line in the echo method:
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
operates directly on the Animal class name. So from the echo method perspective, it doesn't matter how many classes extend it. As long as those classes invoke echo method defined in the Animal class, you will always see Animal printed as a result.
And there is even more than that. If you use the following compiler configuration script:
config.groovy
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
ast(groovy.transform.TypeChecked)
}
and then compile the script (let's call it script.groovy) using this configuration option with the following command:
groovyc --configscript=config.groovy script.groovy
then you will see something like this after decompiling the .class file:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import org.codehaus.groovy.runtime.InvokerHelper;
public class script extends groovy.lang.Script {
public script() {
}
public script(Binding context) {
super(context);
}
public static void main(String... args) {
InvokerHelper.runScript(script.class, args);
}
public Object run() {
Animal.echo();
return Animal.echo();
}
}
You can see that even though you have invoked Dog.echo() and Cat.echo() in your Groovy script, the compiler replaced these calls with the double Animal.echo() invocation. It happened because calling this static method on any other subclass does not make any difference.
Possible solution: applying double dispatch
There is one way to get the expected output - override echo static method in Dog and Cat class. I can assume that your real method may do something more than the exemplary echo method you have shown above, so you might need to call the super echo method from a parent class. But... there are two problems: (1) you can't use super.echo() in the static context, and (2) it doesn't solve the problem, because parent method still operates in the Animal class context.'
To solve this kind of issue you might want to mimic a technique called double dispatch. In short - when we don't have information about the caller in the method that was called, let's allow the caller to pass this information with the method call. Consider the following example:
import groovy.transform.CompileStatic
#CompileStatic
class Animal {
// This is a replacement for the previous echo() method - this one knows the animal type from a parameter
protected static void echo(Class<? extends Animal> clazz) {
println clazz.name
}
static void echo() {
echo(Animal)
}
}
#CompileStatic
class Dog extends Animal {
static void echo() {
echo(Dog)
}
}
#CompileStatic
class Cat extends Animal {
static void echo() {
echo(Cat)
}
}
Animal.echo()
Dog.echo()
Cat.echo()
This may sound like a boilerplate solution - it requires implementing echo method in each subclass. However, it encapsulates the echo logic in the method that requires Class<? extends Animal> parameter, so we can let every subclass to introduce their concrete subtype. Of course, this is not a perfect solution. It requires implementing echo method in each subclass, but there is no other alternative way. Another problem is that it doesn't stop you from calling Dog.echo(Animal) which will cause the same effect as calling Animal.echo(). This double dispatch like approach is more like introducing a shorthand version of echo method which uses the common static echo method implementation for simplicity.
I don't know if this kind of approach solves your problem, but maybe it will help you find a final solution.

How to refer to implementor class with #ClosureParams from trait

I would like to use #ClosureParams with a method in a trait, that takes a Closure as input, which will be passed the trait's implementer when called.
Consider the following example:
trait Fooable {
void foo(#ClosureParams(????) Closure callable) {
callable.call(this)
}
}
class Bar implements Fooable {
String baz
}
new Bar().foo { it.baz == "foo'ed" }
How do I tell the static analyser that the it passed to the closure is actually Bar (last line). What should value should I pass to #ClosureParams in the definition of the foo method?
It won't work with traits at the moment (Groovy 2.4.13), because there is no ClosureSignatureHint implementation that allows you to define at a runtime type of a hint that uses class type that implements method from trait interface. If your trait was implemented only by Bar class then you could specify closure parameter type as:
#CompileStatic
#TypeChecked
trait Fooable {
void foo(#ClosureParams(value = SimpleType, options = ["Bar"]) Closure callable) {
callable.call(this)
}
}
But it's not the case.
#ClosureParams won't even recognize generic type if used with trait. Let's consider following definition:
#CompileStatic
#TypeChecked
trait Fooable<T> {
void foo(#ClosureParams(value = SimpleType, options = ["T"]) Closure callable) {
callable.call(this)
}
}
We could expect that Bar class that implements Fooable<Bar> should work like a charm, but it does not unfortunately:
Closure parameter in this case is recognized as T type. It happens because method foo is implemented inside Bar class and #ClosureParams(value = SimpleType.class,options = {"T"}) is also compiled at Bar class level, so it is not aware of generic type T. Let's take a look at compiled Bar class to understand what's going on:
public class Bar implements Fooable<Bar>, GroovyObject {
private String baz;
public Bar() {
String var1 = "test";
this.baz = var1;
MetaClass var2 = this.$getStaticMetaClass();
this.metaClass = var2;
Helper.$init$(this);
Object var10000 = null;
}
#TraitBridge(
traitClass = Fooable.class,
desc = "(Lgroovy/lang/Closure;)V"
)
public void foo(#ClosureParams(value = SimpleType.class,options = {"T"}) Closure arg1) {
Helper.foo(this, arg1);
Object var10000 = null;
}
// some other methods
}
This is what you will see if you open Bar.class as a decompiled file.
Generics will work fine if instead of trait we would use abstract class. In this case abstract generic class Fooable<T> would implement foo method so Bar class would refer to implementation from Fooable<T> class - a class that is aware of T type. In this case IDE would resolve T correctly and suggest Bar instead.
So what are the options when using trait in this case? You could try implementing your own ClosureSignatureHint class, but this is not that easy. I did a small experiment - I have defined NewSimpleType class and I have copied 1:1 sources from SimpleType class. Then I used it as:
#CompileStatic
#TypeChecked
trait Fooable {
void foo(#ClosureParams(value = NewSimpleType, options = ["Bar"]) Closure callable) {
callable.call(this)
}
}
As you can see I've only replaced Groovy's SimpleType with my custom NewSimpleType. It didn't work. My IDE (IntelliJ IDEA Ultimate 2017.3.3) didn't resolve any type. I've even move this class to a separate Maven project and I've build it and added as a dependency - didn't work as well.
I assume it should be possible to implement a hint class that takes caller class type into account. There are some implementations that take closure parameter type from first, second or third parameter. It sounds doable, at least in theory.
Last option that requires least effort is just provide closure parameter type explicitly, e.g.
Bar bar = new Bar()
bar.foo { Bar b -> b.baz }
It supports all code completion features. The downside is that you can specify different type, like:
Bar bar = new Bar()
bar.foo { String b -> b.toLowerCase() }
IDE won't complain about that, but it will fail while compiling.
Custom StringParameterHint use case
I have created for experiments a static closure signature hint that accepts only java.lang.String as a parameter:
public class StringParameterHint extends ClosureSignatureHint {
#Override
public List<ClassNode[]> getClosureSignatures(MethodNode node, SourceUnit sourceUnit, CompilationUnit compilationUnit, String[] options, ASTNode usage) {
final List<ClassNode[]> list = new ArrayList<>();
list.add(GenericsUtils.parseClassNodesFromString("java.lang.String", sourceUnit, compilationUnit, node, usage));
return list;
}
}
Then I've set it up with #ClosureParams in Fooable.foo(Closure cl) method. Unfortunately IDE does not read this hint and does not recognize it as a type of String:
But compiler (in IDE) is aware of this closure parameter hint and if I cast parameter to Bar like:
bar.foo { Bar b -> b.baz }
then IDE does not mark it as an incorrect expression, yet compilation fails and program does not start:
Error:(11, 19) Groovyc: Expected parameter of type java.lang.String but got tld.company.Bar
Error:(11, 28) Groovyc: [Static type checking] - No such property: baz for class: java.lang.String
So it looks like we can force compiler to be closure parameter aware, but this information is not being read by IDE (IntelliJ IDEA 2017.3.3 in my case). I guess this might be an IDE issue. I've even moved this StringParameterHint class to groovy.transform.stc package (I was assuming that maybe IDE loads all hints from this package automatically), but it didn't help.

Variant Generic Interfaces

I have a generic interface, and a class implementing that interface with a concrete type parameter. I also have a generic class using the generic interface as its type constraint, but the type parameter is restricted to be a subclass of a certain base class. I want to instance the generic class with the class implementing that interface but have a problem of converting the class to that interface. The following code illustrates all the classes I mentioned:
The base class:
class DomainBase
{
}
The class used as the type parameter in the interface
class Person : DomainBase
{
}
The generic interface:
public interface IRepository<T> where T : class
{
IEnumerable<T> Fetch();
T Persist(T item);
}
The class implementing the generic interface:
class PersonRepository : IRepository<Person>
{
public IEnumerable<Person> Fetch()
{
...
}
public Person Persist(Person item)
{
...
}
}
The generic class using the generic interface:
class DomainBaseViewModel<Repository>
where Repository : IRepository<DomainBase>, new()
{
private Repository repository = new Repository();
private ObservableCollection<DomainBase> items;
}
However, the following line can't get compiled because PersonRepository is unable to be converted to IRepository<DomainBase>:
var viewModel = new DomainBaseViewModel<PersonRepository>();
Although I can solve this issue by covariance but it disallows the use of the type parameter in parameter lists:
public interface IRepository<out T> where T : class
{
...
T Persist(object item);
}
class PersonRepository : IRepository<Person>
{
public Person Persist(object item)
{
...
}
}
So I have to convert the parameter to Person, which compromises type safety.
Is there a better way to allow covariance and the use of type parameter in parameter lists in this case?
No - the whole point of the restriction on covariance is that it guarantees safety. A PersonRepository isn't an IRepository<DomainBase> because you can't ask it to persist any arbitrary DomainBase object. What would you expect this code to do?
class Product : DomainBase {}
...
IRepository<DomainBase> repository = new PersonRepository();
repository.Persist(new Product());
PersonRepository doesn't know how to persist Product values.
If in some cases you only need the "read" parts of the repository interface, you could always call that out explicitly:
public interface IRepositoryReader<out T>
{
IEnumerable<T> Fetch();
}
public interface IRepository<T> : IRepositoryReader<T>
{
T Persist(T item);
}
Then your DomainBaseViewModel class could be:
class DomainBaseViewModel<TRepository>
where TRepository : IRepositoryReader<DomainBase>, new()
{
private TRepository repository = new TRepository();
private ObservableCollection<DomainBase> items;
}
That doesn't work if you want your DomainBaseViewModel to persist items as well though. In that case, perhaps it should be generic in the type of model as well:
class DomainBaseViewModel<TRepository, TEntity>
where TRepository : IRepository<TEntity>, new()
{
private TRepository repository = new Repository();
private ObservableCollection<TEntity> items;
}
Then:
var viewModel = new DomainBaseViewModel<PersonRepository, Person>();

Closure Coercion not working in certain case

I'm calling a Java method from Groovy which expects an instance of a SAM interface as a parameter.
Normally Groovy is happy with passing in a closure in these cases, and will coerce it accordingly HOWEVER in this case, the interface extends another one and overrides the single method.
Note - It still only has one method, but it's been overriden.
In this instance Groovy doesn't automatically coerce the closure and the only way I seem to be able to call it is by using "AS".
I'm publishing an API to help kids to learn code and really don't want them to have to use "AS" because it would complicate things.
Here's some code that shows the issue...
Java
public interface BaseHandler<T> {
public void handle(T test);
}
public interface Handler extends BaseHandler<String> {
public void handle(String test);
}
public class LibraryClass {
public void method(Handler handler) {
handler.handle("WORLD!");
}
}
Groovy
LibraryClass bar = new LibraryClass();
bar.method({ name -> println "HELLO " + name})
Error
Caught: groovy.lang.MissingMethodException: No signature of method: Bar.doIt() is applicable for argument types: (testClosures$_run_closure1) values: [testClosures$_run_closure1#fe63b60]
Any help on how to get around this without using "AS" would be hugely appreciated
Groovy wants to implement the interface by coercion, but doesn't know which interface method it should implement. As there are 2:
the handle(String test) and a second one: handle(String test) (of the baseHandler)
The solution is to remove the handle(String test) from the handler (it adds nothing as the BaseHandler posesses this method already thanks to the generics).
Like this it works correctly:
public interface BaseHandler<T> {
public void handle(T test);
}
public interface Handler extends BaseHandler<String> {
}
public class LibraryClass {
public void method(Handler handler) {
handler.handle("WORLD!");
}
}

Is it possible to call default implementations of interfaces with Mockito's doCallRealMethod?

Suppose I have the following interface:
public interface ISomething {
default int doStuff() {
return 2 * getValue();
}
int getValue();
}
When I now mock this interface like this:
#Mock
private ISomething _something;
#Before
public void setup() {
doCallRealMethod().when(_something).doStuff();
}
and try to test the doStuff() method like the following:
#Test
public void testDoStuff() {
when(_something.getValue()).thenReturn(42);
assertThat("doStuff() returns 84", _something.doStuff(), is(84));
}
I expect the test to succeed, but I get:
org.mockito.exceptions.base.MockitoException:
Cannot call real method on java interface. Interface does not have any implementation!
Calling real methods is only possible when mocking concrete classes.
I tried subclassing ISomething with an abstract class like this:
public abstract class Something implements ISomething {
}
and mock this class like above. With this approach, I get the same.
Does Mockito not support calling default implementations?
That's correct. The current version of Mockito doesn't support this. You could raise a feature request here. Do note that it seems to be related to issue 456 which was fixed in release 1.10.0, so please make sure you test this in the latest version first.
I was working on a project using Mockito 1.9.5 and ran into the same issue that you found. We couldn't upgrade Mockito because of the way our build server worked. The problem we ran into was when we were writing unit tests for the concrete subclasses, as we couldn't stub out or include the default methods from the interface in our mock objects (so slightly different from your example).
Here is an example subclass using your model:
public class ConcreteSomething implements ISomething {
#Override
int getValue()
{
return 42;
}
}
Then in the unit test class, we explicitly made a private inner class. This class overrode all the default methods of the concrete class under test (i.e. ConcreteSomething) with the interface's default implementation. So in this example, something like:
private class ConcreteSomethingDefaultImpl extends ConcreteSomething {
#Override
int doStuff() {
return super.doStuff();
}
}
For us, a mock made using mock(ConcreteSomething.class) couldn't have it's default methods called using doCallRealMethod(), but mock(ConcreteSomethingDefaultImpl.class) could, and more importantly, it was the default implementation code in the interface that was being used.
I hope that helps anyone else who is constrained to use a particular version of Mockito.

Resources