使用通配符的Java自引用泛型

时间:2015-09-29 15:34:48

标签: java generics wildcard self-reference

是否可以指定未知泛型类型是自引用的?

尝试失败:

public class MyAction extends ActionSupport {

    private String sender;
    private String subject;
    private String bodyPlain;

    public String execute() {

        LOG.info(sender);
        LOG.info(subject);
        LOG.info(bodyPlain);
        return SUCCESS;
    }
    public void setSender(String sender) {
        this.sender = sender;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public void setBodyPlain(String bodyPlain) {
        this.bodyPlain = bodyPlain;
    }
}

这会导致编译错误:

import java.util.*;

class Generics {
   public enum A { A1, A2 }
   public enum B { B1, B2 }

   public static List<? extends Enum<?>> listFactory(String[] args) {
      if (args.length == 0) {
         return new ArrayList<A>(Arrays.asList(A.A1, A.A2));
      } else {
         return new ArrayList<B>(Arrays.asList(B.B1, B.B2));
      }
   }

   public static void main(String[] args) {
      List<? extends Enum<?>> lst = listFactory(args);
      dblList(lst);
      System.out.println(lst);
   }

   public static <EType extends Enum<EType>> void dblList(List<EType> lst) {
      int size = lst.size();
      for (int i = 0; i < size; i++) {
         lst.add(lst.get(i));
      }
   }
}

理想情况下,Generics.java:17: error: method dblList in class Generics cannot be applied to given types; dblList(lst); ^ required: List<EType> found: List<CAP#1> reason: inferred type does not conform to declared bound(s) inferred: CAP#1 bound(s): Enum<CAP#1> where EType is a type-variable: EType extends Enum<EType> declared in method <EType>dblList(List<EType>) where CAP#1 is a fresh type-variable: CAP#1 extends Enum<?> from capture of ? extends Enum<?> 1 error 的返回类型表示该列表包含自引用泛型类型(其确切类型未知)。

这可能吗?如果是,那么listFactory()listFactory()的类型应该是什么?

2 个答案:

答案 0 :(得分:1)

有效的Java Item 28不鼓励在返回类型中使用通配符:

  

不要将通配符类型用作返回类型。不会为用户提供额外的灵活性,而是强制他们在客户端代码中使用通配符类型。

     

正确使用的通配符类型对于类的用户几乎是不可见的。它们使方法接受它们应该接受的参数并拒绝它们应该拒绝的参数。 如果一个类的用户必须考虑通配符类型,那么该类的API可能有问题。

这是EJ描述的问题的一个很好的例子。 listFactory()实际上只是返回一个List<Enum<?>>但是通过声明一个通配符返回类型,你必须跳过箍来完成看似简单的任务。

如果您改为给listFactory()这样的签名:

public static List<Enum<?>> listFactory(String[] args)

您也可以清除dblList()的签名:

public static <E> void dblList(List<E> lst)

答案 1 :(得分:0)

在Java中,类型参数是具体类型,通配符类型或类型变量。具体类型对于您的用例来说不够灵活,并且通配符不能被约束为自引用(因为每次出现的通配符都可以代表不同的类型)。

这会留下类型变量,它可以被约束为自引用,但是由构造函数或方法的调用者提供,而不是被调用者,所以我们不能这样做:

<E extends Enum<E>> List<E> listFactory(String[] args);

因为有人可能会使用不正确的类型参数调用它。

解决此问题的一种方法是修饰返回类型:

interface EnumList<E extends Enum<E>> extends List<E> {

}

EnumList<?> listFactory(String[] args);

然后呼叫者可以:

EnumList<?> x = listFactory(args);
dblList(x);

其中dblList使用通配符捕获来操作列表:

<E extends Enum<E>> void dblList(List<E> list);

值得注意的是,这使得方法签名更难写,所以只有在有问题的方法实际需要知道类型是自引用时才应该这样做。我之所以提到这一点是因为你的dblList方法没有,可以简单地写成:

<E> void dblList(List<E> list);

代替。