2

我有一个 Generic Class Factory 类,它有两种方法,一种使用 Class 泛型 T 值,另一种只使用它自己的方法泛型定义。

public class GenericClassFactory<T extends ClassMatchable> {
    public <E, K> E newObject(ClassMatcher<E, K> matcher, K key, String packageName){...}
    public <K> T newObject(K key, String packageName){...}
}

使用 T 泛型的方法可以正常工作,但是当我想使用另一种不关心 T 泛型是什么的方法时,它不会使用 Generic E 它只会返回一个 Object 然后我必须键入它.

Data data = new GenericClassFactory().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

这有编译错误,因为它希望我将其类型转换为(数据)。如果我通过 GenericClassFactory 一个有效的类 Generic 它将起作用。如果您定义了 Class Generic 但未使用它,它就像它不识别方法泛型一样。

Data data = new GenericClassFactory<ClassMatchable>().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

这很好用。但是当我的目的不需要它时,我必须定义一个这样的类泛型,这是愚蠢的。我可以这样做:

public class GenericClassFactory {
    public <E, K> E newObject(ClassMatcher<E, K> matcher, K key, String packageName){...}
    public <T extends ClassMatchable, K> T newObject(K key, String packageName){...}
}

但是现在我的第二种方法似乎太宽泛了……也许不是。我的意思是,如果您分配给返回类型的对象没有实现 ClassMatchable,它仍然会给出编译错误。那是我应该走的路吗?这样我就不必打字了?

4

4 回答 4

6

没错,如果不键入类引用,那么即使是只使用方法类型参数的泛型方法也不会被泛化。这是 Java 泛型的更奇怪的细微差别之一。正如您所说,您可以输入一些任意类型T

Data data = new GenericClassFactory<ClassMatchable>().newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

但更有可能这甚至不应该是一个实例方法。不能是静态方法吗?如果是这样,您可以像这样调用它:

Data data =  GenericClassFactory.newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl");

编辑

请注意,这扩展到所有实例成员,而不仅仅是通用实例方法。因此,有一些更简单的案例可以证明这种奇怪的细微差别。此代码编译时只有警告:

public class Scratchpad<T> {
   List<String> list;
   public static void main(String[] args) {
      Scratchpad sp = new Scratchpad();
      List<Integer> list = sp.list;
   }
}

那是因为sp.list被解析为 a List,而不是 a List<String>即使 Scratchpad.list与 无关T

这在JLS 第 4.8 节中有详细记录:

未从其超类或超接口继承的原始类型 C的构造函数(第 8.8 节)、实例方法(第 8.8 节、第 9.4 节)或非静态字段(第 8.3 节)M 的类型是其类型的擦除在对应于 C 的泛型声明中。原始类型 C 的静态成员的类型与其在对应于 C 的泛型声明中的类型相同。

于 2011-04-20T17:37:29.103 回答
1

调用方法时应该告诉 E 和 K 的实际类型:

  new GenericClassFactory<ClassMatchable>().<TypeforE, TypeforK>newObject(...)

Java 似乎无法从参数中推断出它。

而且当然:

如果您定义了 Class Generic 但未使用它,它就像它不识别方法泛型一样。

完全正确。

于 2011-04-20T17:45:18.573 回答
1

如果您定义了 Class Generic 但未使用它,它就像它不识别方法泛型一样。

非常正确。如果你在一个类上定义了一个泛型约束,然后在不提供任何泛型约束的情况下实例化该类(也就是说,你<>完全放弃了),那么你就进入了Raw Types的领域,没有什么是相同的了.

原始类型的存在只是为了向后兼容。根据 Angelika Langer 出色的Java Generics FAQ

不鼓励在将泛型引入 Java 编程语言之后编写的代码中使用原始类型。根据 Java 语言规范,Java 编程语言的未来版本可能会禁止使用原始类型。

它还指出

原始类型的方法或构造函数具有它们在类型擦除后将具有的签名。如果擦除更改了参数类型,则对原始类型的方法或构造函数调用会生成未经检查的警告。

如果该newObject()方法没有使用T它所属的类的类型参数,那么您的设计就有问题:很可能newObject()应该将其设为静态方法。

但是,如果由于某种原因它确实必须是实例方法,则可以使用通配符类型 GenericClassFactory<?>使其工作:

GenericClassFactory<?> gcf = new GenericClassFactory();
Data data = gcf.newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl"); 
于 2011-04-20T18:19:13.043 回答
0

考虑是否T真的是类或方法的类型参数。例如,类中是否有某些东西将创建的类型限制为 T(类级别),或者它可能被用作避免转换结果值(方法级别)的便利。

从您在此处发布的内容来看,在我看来, T 应该是该方法的一种类型,而您的最后一个示例就是答案。如果类实现是通用的并且可以为每个方法调用提供不同的类型,则方法定义似乎不太宽。

于 2011-04-20T17:38:27.127 回答