Axel
Axel

Reputation: 14159

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.

Upvotes: 6

Views: 1858

Answers (2)

assylias
assylias

Reputation: 328649

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;
}

Upvotes: 5

Rohit Jain
Rohit Jain

Reputation: 213281

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.

Upvotes: 4

Related Questions