Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generics in return types of static methods and inheritance

Generics in return types of static methods do not seem to get along well with inheritance. Please take a look at the following code:

class ClassInfo<C>  {
  public ClassInfo(Class<C> clazz) {
    this(clazz,null);
  }  
  public ClassInfo(Class<C> clazz, ClassInfo<? super C> superClassInfo) {
  }
}

class A {
    public static ClassInfo<A> getClassInfo() {
        return new ClassInfo<A>(A.class);
    }
}

class B extends A {
    // Error: The return type is incompatible with A.getClassInfo()
    public static ClassInfo<B> getClassInfo() { 
        return new ClassInfo<B>(B.class, A.getClassInfo());
    }
}

I tried to circumvent this by changing the return type for A.getClassInfo(), and now the error pops up at another location:

class ClassInfo<C>  {
  public ClassInfo(Class<C> clazz) {
    this(clazz,null);
  }  
  public ClassInfo(Class<C> clazz, ClassInfo<? super C> superClassInfo) {
  }
}

class A {
    public static ClassInfo<? extends A> getClassInfo() {
        return new ClassInfo<A>(A.class);
    }
}

class B extends A {
    public static ClassInfo<? extends B> getClassInfo() { 
        // Error: The constructor ClassInfo<B>(Class<B>, ClassInfo<capture#1-of ? extends A>) is undefined
        return new ClassInfo<B>(B.class, A.getClassInfo());
    }
}

What is the reason for this strict checking on static methods? And how can I get along? Changing the method name seems awkward.

like image 310
Axel Avatar asked Nov 23 '12 17:11

Axel


People also ask

Can generics be used with static methods?

Static and non-static generic methods are allowed, as well as generic class constructors. The syntax for a generic method includes a list of type parameters, inside angle brackets, which appears before the method's return type.

What is the return type of static method?

it is a method accessible from outside the class where it is defined (public), it is a static method (static), it does not return any result (return type is void), and.

Can generic types be inherited?

You cannot inherit a generic type. // class Derived20 : T {}// NO!

Can a method return a generic type?

Just like type declarations, method declarations can be generic—that is, parameterized by one or more type parameters.


2 Answers

The static method in B does not override the static method in A but hides it. The JLS 8.4.8.3 explicitly says that the return types must be substitutable or it won't compile:

If a method declaration d1 with return type R1 overrides or hides the declaration of another method d2 with return type R2, then d1 must be return-type-substitutable (§8.4.5) for d2, or a compile-time error occurs.

And substitutability is defined in JLS #8.4.5:

A method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2, if and only if the following conditions hold:

  • [...]
  • If R1 is a reference type then:
    • R1 is either a subtype of R2 or R1 can be converted to a subtype of R2 by unchecked conversion (§5.1.9), or
    • R1 = |R2|

In your case: d1 is the method in B, R1 is ClassInfo<B>, d2 the method in A and R2 is ClassInfo<A>. And ClassInfo<B> is not a subtype of ClassInfo<A>.

However, ClassInfo<? extends B> can be converted to ClassInfo<? extends A>. You can observe that behaviour in:

void someMethod(){
    ClassInfo<B> b1 = (ClassInfo<B>) get1(); //does not compile
    ClassInfo<? extends B> b2 = (ClassInfo<? extends B>) get2(); //compiles
}

ClassInfo<A> get1() {
    return null;
}

ClassInfo<? extends A> get2() {
    return null;
}
like image 102
assylias Avatar answered Oct 13 '22 05:10

assylias


You can't override a static method. So when you declare the same static method, you are creating a new method.

public static ClassInfo<B> getClassInfo() { 
    return new ClassInfo<B>(B.class, A.getClassInfo());
}

But, when you declare your method with changed return type, it is not a valid method hiding, neither an override. So, both the getClassInfo() method of class A and getClassInfo() method of class B, conflict each other. As the method of class A is also visible in class B.

So, in other words, class B has same method, as it has inherited from class A, with change in return type. And since return type of method is not considered a part of method signature. Hence the conflict.

So, you need to have exactly same return type. In which case, class B will ignore the inherited method, and use its own.

like image 38
Rohit Jain Avatar answered Oct 13 '22 06:10

Rohit Jain