Java метод с двумя взаимозависимыми типовыми ограничениями c - PullRequest
0 голосов
/ 24 января 2020

Не уверен, как выразить это словами, поэтому я просто начну с кода, который у меня есть:

// "root type" for all resources
// fixed
public class ResourceClassA
{ }

// "base type" for all resources for a specific domain
// fixed
public class ResourceClassB extends ResourceClassA
{ }

// specific resource type
// can be derived from further but don't think that matters here
// not fixed but heavily constrained in other ways
public class ResourceClassC extends ResourceClassB 
{ }

// only needed for a negative example below, irrelevant otherwise
public class ResourceClassD extends ResourceClassB 
{ }

// fixed
public class Remote
{
    public <T extends ResourceClassA> Set<T> read(Class<T> baseType, Class<? extends T> subType);
}

// semi-fixed: read() signature can be modified
public interface AbstractRemoteAccess<T extends ResourceClassA>
{
    Set<T> read(Class<? extends T> clazz);
}

// semi-fixed: read() signature can be modified
public class SpecificRemoteAccess<T extends ResourceClassA> implements AbstractRemoteAccess<T>
{
    private Class<T> _baseType;
    private Remote _remote;

    public Set<T> read(Class<? extends T> clazz)
    {
        return _remote.read(_baseType, clazz);
    }
}

// not fixed
public class ConsumerClass
{
    public void doSomething(AbstractRemoteAccess<ResourceClassB> remoteAccess)
    {
        Set<ResourceClassB> rawObjects = remoteAccess.read(ResourceClassC.class);

        Set<ResourceClassC> castedObjects = rawObjects.stream()
                                                      .map(c -> (ResourceClassC) c)
                                                      .collect(Collectors.toSet());
    }
}

Все классы, отмеченные fixed , не могут быть изменены, они предоставляются как есть - наоборот для не фиксировано . Класс SpecificRemoteAccess - это тот, который я хочу изменить: я бы хотел, чтобы метод read ()

  • не возвращал свой результат как Установите , но как Установите <> типа generi c соответствия clazz
  • , чтобы вызывающая сторона не должна приводить результат метода, см. ConsumerClass.doSomething ()
  • и все это без потери безопасности типов

Самый простой способ, который я видел, это сделать

Set<V> read(Class<V extends T> clazz)

но это выдает эту ошибку:

Incorrect number of arguments for type Class<T>; it cannot be parameterized with arguments <V, T>

, что, если я правильно его интерпретирую, означает, что компилятор обрабатывает V & T как отдельный тип Аргументы для Class , который не соответствует его определению.

Затем я попытался добавить второй тип generi c V и использовать его как тип generi c для типа возврата читать () . Я начал с

<V extends T> Set<V> read(Class<? extends T> clazz)

, который не ограничивает V до clazz , то есть оба они будут приняты компилятором

Set<ResourceClassC> correct   = remoteAccess.read(ResourceClassC.class);

Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class);

Объявление типа для неверно семантически неверно, но синтаксически хорошо. Поэтому я попытался ограничить V на основе clazz , но единственное решение, о котором я мог подумать, это

<V extends T> Set<V> read(Class<V extends T> clazz, Class<V> classV)

, которое, в некоторой степени, решает проблему сверху:

// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
                                                ResourceClassC.class);

// error: Type mismatch: cannot convert from Set<ResourceClassC> to Set<ResourceClassD>
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class,
                                                  ResourceClassC.class);  

но не только делает вызов read () громоздким (пользователям будет интересно, почему они должны передавать одну и ту же информацию дважды), но и подвержен ошибкам:

// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
                                                ResourceClassC.class); 
// type error
Set<ResourceClassD> incorrect  = remoteAccess.read(ResourceClassC.class,
                                                   ResourceClassC.class);

// compiles but will cause run-time cast failures
Set<ResourceClassD> incorrect2 = remoteAccess.readAndCast2(ResourceClassC.class,
                                                           ResourceClassD.class);

Учитывая, что разработчики на стороне потребителя сталкиваются с сотнями классов ресурсов, таких как ResourceClass C, делая read () подверженным ошибкам просто не вариант.

Буду признателен, если кто-нибудь укажет на мою ошибку.

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