简体   繁体   中英

How to infer the generic type to avoid unchecked casting

Suppose we have a class A that has two subclasses A1, A2. Suppose we have another class B, that also has two subclasses, B1 an B2:

class B{
  List<? extends A> myGenericList;

  B(List<? extends A> myGenericList){
    this.myGenericList = myGenericList;
  }

  public List<? extends A> getMyGenericList(){
    return myGenericList;
  }
}

class B1 extends B{
  B1(List<A1> a1List){
    super(a1List);
  }
}

class B2 extends B{
  B2(List<A2> a2List){
    super(a2List);
  }
}

Now, if we have a class C1 like this:

class C1{
   ...

   public void doSomethingWithB1(B1 b1){
      List<A1> a1list = (List<A1>)b1.getMyGenericList();
   }

   ...
}

How can I implement getMyGenericList of class B so I can avoid the unchecked casting warning?

I tried something like this:

public <T extends A> List<T> getMyGenericList() {
    return this.myGenericList;
}

but the compiler complains with cannot convert from List<capture#3-of ? extends A> to List<T> cannot convert from List<capture#3-of ? extends A> to List<T>

Is there any way to do it?

Thanks in advance.

With the current way you've defined the B class, the myGenericList instance variable could hold a List of any subtype of A, so the unchecked cast warning you get when casting to List<A1> is justified. It could be a List<A> or a List<A2> .

If you don't really care which A you get in the list back, you can just assign it to a List<? extends A> List<? extends A> .

List<? extends A> a1list =  b1.getMyGenericList();

But if you really want to get a List<A1> back from a B1 , then generics on the B class is your answer. Define T at the class level with an upper bound of A . Use it throughout your class to replace your wildcards.

class B<T extends A>
{
  List<T> myGenericList;

  B(List<T> myGenericList){
    this.myGenericList = myGenericList;
  }

  public List<T> getMyGenericList(){
    return myGenericList;
  }
}

Your subclasses of B will define what T is respectively.

class B1 extends B<A1>  // rest of class is the same

class B2 extends B<A2>  // rest of class is the same

This way you have eliminated the unchecked cast warning and even the need to cast at all.

List<A1> a1list = b1.getMyGenericList();

You should move your generic definition to class level as following:

class B<T extends A> {
  List<T> myGenericList;

  B(List<T> myGenericList){
    this.myGenericList = myGenericList;
  }

  public List<T> getMyGenericList(){
    return myGenericList;
  }
}

Now the subclass is defined with concrete parameter A1 :

class B1 extends B<A1>{
  B1(List<A1> a1List){
    super(a1List);
  }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM