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

时间:2021-02-14 22:32:36

Just got a question about generics, why doesn't this compile when using a generic List? If its not possible, anyway around it? Much appreciate any answer.

刚刚得到一个关于泛型的问题,为什么在使用泛型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 个解决方案

#1


1  

This has got to do with the subtyping rules for parametrized types. I'll explain it in three steps:

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

Non-nested case

When you have the following subtype relation (where <: is the symbol for "is a subtype of"):

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

_ModelDto <: BaseObject

The following relation does not hold:

以下关系不成立:

List<_ModelDto> <: List<BaseObject>

But the following relations do:

但以下关系如下:

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

This is the reason why Java has wildcards: to enable these kind of subtype relations. All of this is explained in the Generics tutorial. If you understand this, we can continue with the nested case...

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

Nested case

Let's do exactly the same, but with one more level of nesting. Starting from the subtype relation:

让我们做同样的事情,但有一个更高级别的嵌套。从子类型关系开始:

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

The following relation does not hold, for exactly the same reasons as above:

由于与上述完全相同的原因,以下关系不成立:

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

This is precisely the conversion you are trying to do when calling service.getList(callBack), and since the subtype relation does not hold, the conversion fails.

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

However, as above, you do have the following relations:

但是,如上所述,您确实具有以下关系:

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

Solution

So you should write the signature of getList as follows to make the call work:

因此,您应该按如下方式编写getList的签名以使调用工作:

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

The difference will be that the body of getList will be constrained with how it can use the callback. If MyAsync contains the following members:

不同之处在于getList的主体将受限于它如何使用回调。如果MyAsync包含以下成员:

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

Then, the body of getList will be able to get a list from the callback. However, it cannot set the list (except setting it to null), because it does not know exactly what kind of list is represented by the ?.

然后,getList的主体将能够从回调中获取列表。但是,它无法设置列表(除了将其设置为null),因为它不确切地知道什么类型的列表由?表示。

In contrast, with your original signature, set is available, and that is why the compiler cannot allow your argument.

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

#2


5  

The fact that your MyAsync interface doesn't contain any method signatures and doesn't have a particularly informative name is a code smell from my perspective, but I'll assume that this is just a dummy example. As it is written, getList() couldn't ever have any reasonable implementation that used the callback in any way; remember that type erasure will erase this method signature to getList(MyAsync callback);

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

The reason that this doesn't compile is that your bound is wrong. MyAsync<List<? extends BaseObject>> gives T as List<? extends BaseObject>, a list of some unknown type.

这不能编译的原因是你的绑定是错误的。 MyAsync <名单 >赋予T作为List ,一个未知类型的列表。

It looks to me like what you want is for the getList method itself to be generic:

它看起来像你想要的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
    }
}

#3


2  

The '?' in generic types can be pretty confusing. Honestly I'm not sure why this won't compile. It has to do with using the '?' in a nested generic type. But I do know some ways to work around it.

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

Is there a reason that the declaration of the MyAsync in MyClass has to reference _ModelDto? It would work if you changed it to look like this:

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

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

   };

   service.getList(callBack);

If you need to reference the type _ModelDto directly you could change the definition of ServiceAsync and it will fix the problem.
Change it to look like this:

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

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


Then add the parameter type to the declaration in MyClass

然后将参数类型添加到MyClass中的声明中

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

      };

      service.getList(callBack);
   }
}

#1


1  

This has got to do with the subtyping rules for parametrized types. I'll explain it in three steps:

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

Non-nested case

When you have the following subtype relation (where <: is the symbol for "is a subtype of"):

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

_ModelDto <: BaseObject

The following relation does not hold:

以下关系不成立:

List<_ModelDto> <: List<BaseObject>

But the following relations do:

但以下关系如下:

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

This is the reason why Java has wildcards: to enable these kind of subtype relations. All of this is explained in the Generics tutorial. If you understand this, we can continue with the nested case...

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

Nested case

Let's do exactly the same, but with one more level of nesting. Starting from the subtype relation:

让我们做同样的事情,但有一个更高级别的嵌套。从子类型关系开始:

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

The following relation does not hold, for exactly the same reasons as above:

由于与上述完全相同的原因,以下关系不成立:

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

This is precisely the conversion you are trying to do when calling service.getList(callBack), and since the subtype relation does not hold, the conversion fails.

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

However, as above, you do have the following relations:

但是,如上所述,您确实具有以下关系:

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

Solution

So you should write the signature of getList as follows to make the call work:

因此,您应该按如下方式编写getList的签名以使调用工作:

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

The difference will be that the body of getList will be constrained with how it can use the callback. If MyAsync contains the following members:

不同之处在于getList的主体将受限于它如何使用回调。如果MyAsync包含以下成员:

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

Then, the body of getList will be able to get a list from the callback. However, it cannot set the list (except setting it to null), because it does not know exactly what kind of list is represented by the ?.

然后,getList的主体将能够从回调中获取列表。但是,它无法设置列表(除了将其设置为null),因为它不确切地知道什么类型的列表由?表示。

In contrast, with your original signature, set is available, and that is why the compiler cannot allow your argument.

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

#2


5  

The fact that your MyAsync interface doesn't contain any method signatures and doesn't have a particularly informative name is a code smell from my perspective, but I'll assume that this is just a dummy example. As it is written, getList() couldn't ever have any reasonable implementation that used the callback in any way; remember that type erasure will erase this method signature to getList(MyAsync callback);

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

The reason that this doesn't compile is that your bound is wrong. MyAsync<List<? extends BaseObject>> gives T as List<? extends BaseObject>, a list of some unknown type.

这不能编译的原因是你的绑定是错误的。 MyAsync <名单 >赋予T作为List ,一个未知类型的列表。

It looks to me like what you want is for the getList method itself to be generic:

它看起来像你想要的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
    }
}

#3


2  

The '?' in generic types can be pretty confusing. Honestly I'm not sure why this won't compile. It has to do with using the '?' in a nested generic type. But I do know some ways to work around it.

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

Is there a reason that the declaration of the MyAsync in MyClass has to reference _ModelDto? It would work if you changed it to look like this:

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

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

   };

   service.getList(callBack);

If you need to reference the type _ModelDto directly you could change the definition of ServiceAsync and it will fix the problem.
Change it to look like this:

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

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


Then add the parameter type to the declaration in MyClass

然后将参数类型添加到MyClass中的声明中

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

      };

      service.getList(callBack);
   }
}