ServiceLoader, где тип parm сам по себе является общим

class ServiceLoader<S> implements Iterable<S> {
    // ...
}

interface Foo<T> {
    // ...
}

class FooRepository {
    void add(Iterable<Foo<?>> foos) {
        // ...
    }
}

FooRepository repo = new FooRepository();
repo.add(ServiceLoader.load(Foo.class));

Это приводит к ошибке компилятора: "Метод add(Iterable<Foo<?>>) в типе FooRepository не применим для аргументов (ServiceLoader<Foo>)".

Я надеялся, что смогу вылечить Iterable<Foo> как Iterable<Foo<?>>, но само по себе это не похоже на работу. Что было бы самым чистым способом трансмутировать результат ServiceLoader.load(Foo.class) в то, что я могу накормить FooRepository.add()?

3 ответа

Решение

Если вы говорите о java.util.ServiceLoader<S> затем его Javadoc говорит:

Класс провайдера обычно является не самим провайдером, а прокси-сервером, который содержит достаточно информации, чтобы решить, способен ли провайдер удовлетворить определенный запрос вместе с кодом, который может создать фактического провайдера по требованию. [...] Единственным требованием, предъявляемым этой возможностью, является то, что классы провайдеров должны иметь конструктор с нулевым аргументом, чтобы их можно было создавать во время загрузки.

В сочетании с тем, что класс провайдера явно создается с помощью отражения, это говорит мне о том, что класс провайдера, объявленный с аргументом универсального типа, будет фактически создан как необработанный тип.

Таким образом, потому что вы передаете объект типа Class<Foo> - и в Java нет способа создать объект типа Class<Foo<Bar>> - то, что вы получите, это то, что вы просили, Iterable<Foo>, где Foo это необработанный тип, созданный из Foo<T>,

К сожалению, я думаю простой ответ: не делай этого тогда! Либо сделайте класс вашего провайдера конкретным подклассом универсального типа, либо сделайте класс провайдера просто фабрикой, которая может создавать новые объекты соответствующего универсального типа.

Честно говоря, я не могу придумать какой-либо веской причины для создания универсального класса поставщика услуг - где бы вы предоставили аргументы типа? Я думаю, что заводской механизм - ваш лучший выбор.

Это потому, что универсальные типы предназначены только для времени компиляции. Они стираются во время выполнения (тип стирания). Универсальный тип T интерфейса Foo больше не будет известен во время выполнения, поэтому метод "add" класса Repo не позволит вам (в общем) вводить тип аргумента (не позволит вам иметь аргумент типа Foo<?>, поскольку он не может гарантировать, что внутри метода вы сможете обработать этот аргумент как аргумент типа F<?>, Он только знает, что он относится к типу Foo, потому что это фактический тип, но он не позволит вам набрать его в дальнейшем. Ваш код будет работать, если вы измените метод на

void add(Iterable<Foo> foos)

Вина ложится на систему типов Java. Вам нужен грубый бросок.

Любой актерский состав Foo.class в Class<Foo<?>>, или же ServiceLoader<Foo> в ServiceLoader<Foo<?>>, Оба явно безвредны.

Другие вопросы по тегам