Не удается скомпилировать класс, вызывающий метод в интерфейсе с аргументом общего списка - PullRequest
2 голосов
/ 15 октября 2008

Только что возник вопрос о дженериках, почему он не компилируется при использовании универсального списка? Если это невозможно, в любом случае? Очень ценю любой ответ.

// 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 ]

5 голосов
/ 15 октября 2008

Тот факт, что ваш интерфейс 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
    }
}
2 голосов
/ 15 октября 2008

'?' в общих типах может быть довольно запутанным. Честно говоря, я не уверен, почему это не скомпилируется. Это связано с использованием «?» во вложенном универсальном типе. Но я знаю несколько способов обойти это.

Есть ли причина, по которой объявление MyAsync в MyClass должно ссылаться на _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);
   }
}
1 голос
/ 15 октября 2008

Это связано с правилами подтипов для параметризованных типов. Я объясню это в три этапа:

Случай без вложенности

Если у вас есть следующее отношение подтипа (где <: является символом для «является подтипом»):

_ModelDto <: BaseObject

Следующее отношение не выполняется:

List<_ModelDto> <: List<BaseObject>

Но существуют следующие отношения:

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

Это причина, по которой в Java есть символы подстановки: для включения такого рода отношений подтипов. Все это объясняется в учебнике Generics . Если вы понимаете это, мы можем продолжить с вложенным случаем ...

Вложенный чехол

Давайте сделаем точно так же, но с еще одним уровнем вложенности. Начиная с отношения подтип:

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

Следующее соотношение не выполняется по тем же причинам, что и выше:

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

Это точно преобразование, которое вы пытаетесь выполнить при вызове service.getList(callBack), и поскольку отношение подтипа не выполняется, преобразование завершается неудачей.

Однако, как и выше, у вас do есть следующие отношения:

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 доступен, и поэтому компилятор не может разрешить ваш аргумент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...