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

发布于 2024-12-09 10:45:42 字数 630 浏览 1 评论 0 原文

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>) 不适用于参数 (ServiceLoader)”。

我希望能够将 Iterable 视为 Iterable>,但它本身似乎不起作用。将 ServiceLoader.load(Foo.class) 的结果转换为可以提供给 FooRepository.add() 的最干净的方法是什么?

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

This yields a compiler error: "The method add(Iterable<Foo<?>>) in the type FooRepository is not applicable for the arguments (ServiceLoader<Foo>)".

I was hoping to be able to treat an Iterable<Foo> as an Iterable<Foo<?>>, but on its own it doesn't seem to work. What would be the cleanest way to transmute the result of ServiceLoader.load(Foo.class) into something I can feed to FooRepository.add()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

扛刀软妹 2024-12-16 10:45:42

如果您正在谈论 java.util。 ServiceLoader,那么它的 Javadoc 说:

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

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

因此,因为您传递的是 Class 类型的对象,而 Java 中无法构造 Class> 类型的对象。 code> —— 那么你得到的就是你所要求的,一个 Iterable,其中 Foo 是从创建的原始类型Foo

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

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

If you're talking about java.util.ServiceLoader<S>, then its Javadoc says:

The provider class is typically not the entire provider itself but rather a proxy which contains enough information to decide whether the provider is able to satisfy a particular request together with code that can create the actual provider on demand. [...] The only requirement enforced by this facility is that provider classes must have a zero-argument constructor so that they can be instantiated during loading.

Combined with the way that the provider class is clearly instantiated using reflection, that tells me that a provider class declared with a generic type argument will in fact be instantiated as a raw type.

Thus, because you're passing in an object of type Class<Foo> -- and there's no way in Java to construct a object of type Class<Foo<Bar>> -- then what you get back is what you asked for, an Iterable<Foo>, where Foo is the raw type created from Foo<T>.

Unfortunately, I think the simple answer is: Don't do that then! Either make your provider class be a concrete subclass of the generic type, or else make your provider class be simply a factory that can construct new objects of the appropriate generic type.

Honestly, I can't think of any good reason to make a generic service provider class anyway -- where would you provide the type arguments? I think the factory mechanism is your best bet.

风苍溪 2024-12-16 10:45:42

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

Foo,因为这是一个实际类型,但如果您将方法更改为void add(Iterable foos)

That's because generic types are for compile time only. They are erased at runtime (type erasure). The generic type T of the Foo interface will no longer be known at runtime, so the "add" method of the Repo class will not let you further (generically) type the argument type (will not let you have the argument of type Foo<?>, since it can't guarantee that inside the method you'll be able to actually treat that argument as an argument of type F<?>. It only knows that it's of type Foo, because that's an actual type, but it won't let you generically type it further. Your code will run if you change the method to

void add(Iterable<Foo> foos)

牵强ㄟ 2024-12-16 10:45:42

这一切都归咎于 Java 的类型系统。你需要残酷的演员阵容。

Foo.class 转换为 Class>,或将 ServiceLoader 转换为 ServiceLoader ?>>。两者显然都是无害的。

The blame falls on Java's type system. You need brute cast.

Either cast Foo.class to Class<Foo<?>>, or ServiceLoader<Foo> to ServiceLoader<Foo<?>>. Both are obviously harmless.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文