ServiceLoader,其中parm类型本身是通用的

时间:2011-10-11 18:32:11

标签: java generics serviceloader

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));

这会产生编译器错误:“FooRepository类型中的方法add(Iterable<Foo<?>>)不适用于参数(ServiceLoader<Foo>)”。

我希望能够将Iterable<Foo>视为Iterable<Foo<?>>,但就其自身而言似乎无效。将ServiceLoader.load(Foo.class)的结果转化为我可以提供给FooRepository.add()的内容的最简洁方法是什么?

3 个答案:

答案 0 :(得分:1)

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

  

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

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

因此,因为你传入的是Class<Foo>类型的对象 - 并且在Java中没有办法构造类型为Class<Foo<Bar>>的对象 - 那么你得到的就是你所要求的for,Iterable<Foo>,其中Foo是从Foo<T>创建的原始类型。

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

老实说,我无法想出任何有理由制作通用服务提供者类 - 你会在哪里提供类型参数?我认为工厂机制是你最好的选择。

答案 1 :(得分:0)

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

,你的代码就会运行

F<?>

答案 2 :(得分:-1)

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

Foo.class投射到Class<Foo<?>>,或ServiceLoader<Foo>投放到ServiceLoader<Foo<?>>。两者都显然是无害的。