1
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>)”。

我希望能够将 anIterable<Foo>视为Iterable<Foo<?>>,但就其本身而言,它似乎不起作用。将结果转化ServiceLoader.load(Foo.class)为我可以喂食的最干净的方法是什么FooRepository.add()

4

3 回答 3

1

如果您在谈论java.util.ServiceLoader<S>,那么它的 Javadoc 会说:

提供者类通常不是整个提供者本身,而是一个代理,它包含足够的信息来决定提供者是否能够满足特定请求以及可以按需创建实际提供者的代码。[...] 此工具强制执行的唯一要求是提供程序类必须具有零参数构造函数,以便它们可以在加载期间被实例化。

结合使用反射清楚地实例化提供程序类的方式,这告诉我使用泛型类型参数声明的提供程序类实际上将被实例化为原始类型

因此,因为你传入了一个类型的对象Class<Foo>——Java 中没有办法构造一个类型的对象Class<Foo<Bar>>——那么你得到的就是你所要求的,一个Iterable<Foo>Foo原始类型是从哪里创建的Foo<T>

不幸的是,我认为简单的答案是:那就不要那样做!要么使您的提供者类成为泛型类型的具体子类,要么使您的提供者类成为可以构造适当泛型类型的新对象的简单工厂。

老实说,我想不出任何好的理由来创建一个通用的服务提供者类——你会在哪里提供类型参数?我认为工厂机制是你最好的选择。

于 2011-10-11T19:16:47.983 回答
0

那是因为泛型类型仅用于编译时。它们在运行时被擦除(类型擦除)。Foo 接口的泛型类型 T 在运行时将不再为人所知,因此 Repo 类的“add”方法不会让您进一步(通常)输入参数类型(不会让您拥有 type 的参数Foo<?>,因为它不能保证在方法内部你将能够实际将该参数视为类型的参数F<?>。它只知道它是 Foo 类型,因为这是一个实际类型,但它不会让你一般地输入它进一步。如果您将方法更改为,您的代码将运行

void add(Iterable<Foo> foos)

于 2011-10-11T19:00:34.200 回答
-2

责任归咎于 Java 的类型系统。你需要蛮力演员。

要么投到Foo.classClass<Foo<?>>要么ServiceLoader<Foo>投到ServiceLoader<Foo<?>>。两者显然都是无害的。

于 2011-10-11T21:24:34.893 回答