无法编译类在具有泛型列表参数的接口中调用方法

时间:2008-10-15 03:53:23

标签: java generics

刚刚得到一个关于泛型的问题,为什么在使用泛型List时这不会编译?如果它不可能,反正它周围?非常感谢任何答案。

// Interface used in the ServiceAsync inteface.
public interface BaseObject
{
    public String getId();
}

// Class that implements the interface
public class _ModelDto implements BaseObject, IsSerializable
{
    protected String id;

    public void setId(String id)
    {
        this.id = id;
    }

    public String getId()
    {
        return id;
    }
}

// Interface used in the ServiceAsync inteface.
public interface MyAsync<T>
{
     // Nothing here.
}

// Service interface use both interfaces above.
public interface ServiceAsync
{
    public void getList(MyAsync<List<? extends BaseObject>> callback);
}

public class MyClass
{
    ServiceAsync service = (some implementation);
    MyAsync<List<_ModelDto>> callBack = new MyAsync<List<_ModelDto>>()
    {

    };

    service.getList(callBack);  // This does not compile, says arguments are not applicable????
}

3 个答案:

答案 0 :(得分:5)

MyAsync接口不包含任何方法签名且没有特别信息的名称这一事实从我的角度来看是代码味道,但我会假设这只是一个虚拟的例子。在编写时,getList()无法以任何方式使用回调的任何合理实现;请记住,类型擦除会将此方法签名删除到getList(MyAsync callback);

这不能编译的原因是你的绑定是错误的。 MyAsync<List<? extends BaseObject>>将T视为List<? extends BaseObject>,列出了一些未知类型。

在我看来,你想要的是getList方法本身是通用的:

public interface ServiceAsync {
    public <T extends BaseObject> void getList(MyAsync<List<T>> callback);
}

public class MyClass {
    public void foo() {
        ServiceAsync service = null;
        MyAsync<List<_ModelDto>> callBack = new MyAsync<List<_ModelDto>>() {};

        service.getList (callBack);  // This compiles
    }
}

答案 1 :(得分:2)

'?'在泛型类型中可能会非常混乱。老实说,我不知道为什么这不会编译。它与使用'?'有关在嵌套的泛型类型中。但我确实知道一些解决方法。

是否有理由在MyClass中声明MyAsync必须引用_ModelDto?如果你改变它看起来像这样:

   ServiceAsync service = (some implementation);
   MyAsync<List<? extends BaseObject>> callBack = new MyAsync<List<? extends BaseObject>>() 
   {

   };

   service.getList(callBack);

如果需要直接引用_ModelDto类型,可以更改ServiceAsync的定义,它将解决问题。
将其更改为如下所示:

public interface ServiceAsync<T extends BaseObject>
{
    public void getList(MyAsync<List<T>> callback);
}


然后将参数类型添加到MyClass

中的声明中
public class MyClass 
{
   public void method() 
   {
      ServiceAsync<_ModelDto> service = (some implementation);
      MyAsync<List<_ModelDto>> callBack = new MyAsync<List<_ModelDto>>() 
      {

      };

      service.getList(callBack);
   }
}

答案 2 :(得分:1)

这与参数化类型的子类型规则有关。我将分三步解释:

非嵌套案例

如果您有以下子类型关系(其中<:是“是子类型”的符号):

_ModelDto <: BaseObject

以下关系持有:

List<_ModelDto> <: List<BaseObject>

但以下关系如下:

List<_ModelDto> <: List<? extends _ModelDto> <: List<? extends BaseObject>

这就是Java具有通配符的原因:启用这些子类型关系。所有这些都在Generics tutorial中解释。如果您理解这一点,我们可以继续使用嵌套案例......

嵌套案例

让我们做同样的事情,但还有一层嵌套。从子类型关系开始:

List<_ModelDto> <: List<? extends BaseObject>

以下关系保留,原因与上述完全相同:

MyAsync<List<_ModelDto>> <: MyAsync<List<? extends BaseObject>>

这是正好您在调用service.getList(callBack)时尝试进行的转换,并且由于子类型关系不成立,转换失败。

但是,如上所述,执行具有以下关系:

MyAsync<List<_ModelDto>>
  <: MyAsync<? extends List<_ModelDto>>
  <: MyAsync<? extends List<? extends BaseObject>>

解决方案

因此,您应该按照以下方式编写getList的签名以使呼叫正常工作:

public void getList(MyAsync<? extends List<? extends BaseObject>> callback);

不同之处在于getList的正文将受限于callback的使用方式。如果MyAsync包含以下成员:

public interface MyAsync<T> {
    T get();
    void set(T t);
}

然后,getList的正文将能够get来自回调的列表。但是,它不能set列表(除了将其设置为null),因为它并不确切知道?表示的列表类型。

相比之下,使用原始签名,set可用,这就是编译器无法允许您的参数的原因。