开发者

Java generics extending return type of methods

Take a look at these three classes. Minatchi allows itself to be extended so that its methods' returning type could be extended as well. To illustrate, I used a static method.

public class Minatchi<T extends Minatchi<?>>{

  static public <T extends Minatchi<?>>
    List<T> listAll(){
      return
        (List<T>) query();
  }
}

And so I subclass Minatchi into Lady

public class Lady
extends Minatchi<Lady>{

}

This is where the questionable behaviour takes place.

public class HelloMinatchi{

  private void listA(){
    List<Lady> uvs = Lady.listAll();
    for (Lady uv: uvs){
      logger.info(uv.getName() );
    }
  }

  private void listB(){
    for (Lady uv: Lady.listAll()){
      logger.info(uv.getName() );
    }
 开发者_JS百科 }
}

Methods listA and listB are essentially the same. listA places the list into an intermediate variable uvs, whereas listB directly places listAll into the for-loop header.

However, for listB, the compiler complains Cannot convert Minatchi<?> to Lady.

So this question is about the design integrity of Java generics. Yet another generics gripe.

Is this a deliberate design feature or an unintentional design bug that Java generics designers did not know how to solve. If deliberate, why did they do that? If bug, are they planning to solve it?

Or is this my personal problem that I do not know a better way to declare the generics? If so, tell me how.

(I used a generic Minatchi class because I have non-static methods to be exposed to class extension too, which I left out in the question.)


The static method does not take the generic type definition from the class. i.e. the listAll() method does not know about the Lady (in extends Minatchi<Lady>).

Its return type is inferred by the left-hand side of the expression:

  • in listA() the left-hand side defines that it expects List<Lady>
  • in listB() the forEach loop looks like it should also expect a Lady, but it appears that the compiler isn't properly instructed withing the forEach loop.

The way to make listB() work is to tell it what generic type to use:

for (Lady uv : Lady.<Lady>listAll()) {..}


Unfortunately, as I mentioned in another question: Why implicit type inference only works in an assignment?, implicit type inference in Java only works in an assignment.

I still don't know the reason. It still seems stupid for me, though.


This is happening because of type erasure.

From the Java Generic tutorial

When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method.

Because the call Lady.ListAll is being performed on the raw type Minatchi, the compiler is unable to know that the specific type is Minatchi

Type erasure was used for generics so that generic types would be compatible with Java libraries compiled before generics were added to the Library. The has been some effort to have reification added to Java, but it is not on the roadmap for Java 7.


Your problem is that you let the compiler infer the generic arguments to the listAll method and in the first case it infers what you want because you store the result in a variable and it can just look at the type of the variable. In the second it can't infer the "right" type automatically, so you need to specify it yourself:

for (Lady uv: Lady.<Lady>listAll()){
    logger.info(uv.getName() );
}

Please note that in this example there's no reason for the Minatchi class to be generic as that does not affect the static method at all.

Please also note that calling Lady.listAll is exactly the same as calling Minatchi.listAll (i.e. it does not affect what types the compiler could or will infer as the generic arguments).

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜