How to call a super constructor in groovy that accepts varargs? - groovy

I am using groovy as an extension language in my application. The constructor of the class which a script extend accepts variable arguments. When I try to instantiate the groovy class, I get an java.lang.ArrayIndexOutOfBoundsException from the super() call in the constructor. The issue can easily be reproduced in a standalone groovy script:
// problem.groovy
class A {
A(float ... more) {}
}
class B extends A {
B() {
super();
}
}
new B();
when run, this produces:
$ groovy problem.groovy
Caught: java.lang.ArrayIndexOutOfBoundsException: 0
java.lang.ArrayIndexOutOfBoundsException: 0
at B.<init>(problem.groovy:7)
at problem.run(problem.groovy:11)
line 7 is the super() call in class B.
Is this a bug in the language itself? I couldn't find any other mention of it online. I'm new to Groovy and I may well not be understanding some subtlety of the language. At the very least, it seems like this should throw a compiler error when loading the script.

You can use #InheritConstructors AST to avoid this boilerplate code.
class A {
A(float ... more) {}
}
#groovy.transform.InheritConstructors
class B extends A {}
new B()
Moreover, in your example wouldn't you provide a default constructor in A (since it is being overloaded) and then use super() in B's constructor. Or initialize the overloaded constructors args to null.
class A {
A(){println 'default'}
//Or use A(float... more = null) {println 'varargs'}
//instead of default constructor
A(float... more) {println 'varargs'}
}
class B extends A {
B(){
super()
}
}
new B()
//Uses default constructor A() if overloaded
//constructor is not initialized to null

It seems to be calling a nonexistent constructor with super() - call it even with a null or put a no arg constructor in A and it works.
It appears that the variable arg constructor on the superclass isn't matching. Fun.

Related

Reference Error: calling class method in super() typescript

I am not able to call someMethod, it gives error
ReferenceError: Must call super constructor in derived class before
accessing 'this' or returning from derived constructor\
class BaseClass{
//some properties here I want to return whoever extends BaseClass
}
class ChildClass extend BaseClass{
constructor(){
super(
this.someMethod();
)
}
someMethod() {}
}
In your constructor, you have to call super() before you can use this:
class BaseClass{
//some properties here I want to return whoever calls ChildClass
}
class ChildClass extend BaseClass{
constructor(){
super()
this.someMethod();
}
someMethod() {}
}
There is no permitted structure for super(this.someMethod()) because you can't use this before you've called super(...). There is something like super.someMethod() which lets you call base class methods (without your local override), but that's not a substitute for calling super() in your constructor.
You can call super(someArgs) with arguments if you want and that would be whatever arguments you want to be passed to the base class constructor. But, you can't use this before doing so.

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.

groovy immutable object with parent class

I have two immutable groovy classes that have a few shared values that I'm trying to abstract to a parent class. However when I create the following, the second test case always fails. Although everything compiles correctly and no error is thrown at runtime, when I assign the parent property int he constructor, it is never set, resulting in a null value. I havent found any documentation that forbids this, but I'm wondering is this even possible? I've tried a number of configuration of Annotations and class-types (e.g. removing abstract from the parent) but nothing seems to work short of just removing the #Immutable tag altogether.
abstract class TestParent {
String parentProperty1
}
#ToString(includeNames = true)
#Immutable
class TestChild extends TestParent {
String childProperty1
String childProperty2
}
class TestCase {
#Test
void TestOne() {
TestChild testChild = new TestChild(
childProperty1: "childOne",
childProperty2: "childTwo",
parentProperty1: "parentOne"
)
assert testChild
assert testChild.parentProperty1
}
}
Based on the code for the ImmutableASTTransformation, the Map-arg constructor added by the createConstructorMapCommon method does not include a call to super(args) in the method body.
which means that immutable classes are self contained by default
Now if you want to do it you need to use composition instead of inheritance and this is an example of how you can do it :
import groovy.transform.*
#TupleConstructor
class A {
String a
}
#Immutable(knownImmutableClasses=[A])
class B {
#Delegate A base
String b
}
def b = new B(base: new A("a"), b: "b")
assert b.a
i hope this will help :)

Groovy: Inherit trait method

So I have the following case:
trait JsonRepresentable {
def foo() { print "json" }
}
class SuperA implements JsonRepresentable { }
class SuperB implements JsonRepresentable { }
class Child1 extends SuperA {}
class Child2 extends SuperB { }
Now, if I call the foo() method on SuperA or SuperB it works fine. However if I inherit from them the foo() method is not implemented on the child classes.
Groovy:Can't have an abstract method in a non-abstract class. The class 'Child1' must be declared abstract or the method 'foo()' must be implemented.
It can be solved if I implement the JsonRepresentable trait on the child too, but its already defined on the super class so I guess there is a way to inherit the trait's foo method some way. Can you help me how to do that?
Edit:
Groovy version: 2.3.10
The problem was not in groovy. It was the eclipse groovy plugin (or compiler) which marked the build erroneously. I couldn't solve the compilation issue, but it runs fine when I start the application, so it's ok with me.

#Delegate class without default constructor

How can I create a delegate class in Groovy for a class which doesn't have a default constructor? I would like to decorate JUnit's ResultPrinter but am getting an error about the missing constructor.
I don't understand your issue. I just tried this with Java's Short — which also does not have a default constructor.
Everything worked as expected, except if you didn't initialize the delegated object, you get an NPE.
Is it possible you are using #Delegate incorrectly? Delegate doesn't decorate existing classes, it allows you to use an existing classes methods in your own class. It's like extend, but without the class inheritance.
Example code:
class Foo {
#Delegate Short num
String bar
String toString() { "$bar: $num" }
}
def f = new Foo(bar: 'bob', num: 34 as Short)
println f // OK
println f.doubleValue() // OK
f = new Foo()
println f.doubleValue() // NPE
(Alternatively, providing some useful information, such as the actual error and stacktrace, and example code, will get you more useful responses.)

Resources