Andrew Stepanov
Andrew Stepanov

Reputation: 71

Groovy Generics problems with <T extends SuperClass>

class TestClass <T extends SuperClass>{
 public List<T> doSmth(){
    ///....
    List testObjects = []
    testObjects.add(new T(arg))
   return testObjects;
 }
}

class SuperClass{

}
class A extends SuperClass{
 A(Arg arg){
  ....
}
class B extends SuperClass{
 B(Arg arg){
  ....
}

////////test
class Main{
 List <A> a
 List <B> b

 Main(){
  this.a = new TestClass<A>().doSmth()
  this.b = new TestClass<B>().doSmth()
 }
}

Exception found (groovy.lang.GroovyRuntimeException: Could not find matching constructor for SuperClass) on line

 testObjects.add(new T(arg))

This is quite correct coz SuperClass have no constructor. But sub-classes have one and all of them have the same signature, arg with the same type.

can some one help me to find a solution to my problem?

Upvotes: 5

Views: 2136

Answers (2)

Evgeny
Evgeny

Reputation: 2568

The reason why your code doesn't work is not matching constructors of A and B. The reason is missing constructor that accepts string in SuperClass.
new T() doesn't work as you expect. In your code it becomes new SuperClass(arg). The following code demonstrates it

class TestClass <T extends SuperClass> {
    public void doSmth(){
        println new T().class.simpleName;
    }
}

class SuperClass{}
class A extends SuperClass{}
class B extends SuperClass{}

new TestClass<A>().doSmth()
new TestClass<B>().doSmth()
new TestClass<String>().doSmth() //even this works

Output is

SuperClass
SuperClass
SuperClass

TestClass <T> will print Object. TestClass <T extends A> will print A and so on.

Groovy doesn't have generic type information in runtime. Groovy even doesn't do compile type checking (example with new TestClass<String>()). But it keeps some info to make new T() work.

Java's generics implementation incorporates a feature known as "type erasure" which "throws away" generic type information after completing static type checking. This allows Java to easily integrate with legacy "non-generics" libraries. Groovy currently does a little further and throws away generics information "at the source level". Generics information is kept within signatures where appropriate http://web.archive.org/web/20150102195947/http://groovy.codehaus.org/Generics

Upvotes: 4

Steinar
Steinar

Reputation: 5950

Unfortunately, it's not possible to create a new instance of the generic type with new T. So you'll need to work around that problem.

One way to work around it is to include a closure parameter in doSmth() that creates the instance for you.

public List<T> doSmth(Closure newT) {
    List testObjects = []
    testObjects.add(newT(arg))
    return testObjects;
}

and provide the code to create new instance when calling the method:

Main() {
    this.a = new TestClass<A>().doSmth { new A(it) }
    this.b = new TestClass<B>().doSmth { new B(it) }
}

Upvotes: 3

Related Questions