Java 类泛型和方法泛型冲突

发布于 2024-11-02 16:48:02 字数 1243 浏览 5 评论 0 原文

我有一个通用类工厂类,它有两种方法,一种使用类通用 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 泛型是什么的方法时,它不会使用泛型 E,它只会返回一个对象,然后我必须对其进行类型转换。

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

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

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,它仍然会给出编译错误。那是我应该走的路吗?这样我就不用打字了?

I have a Generic Class Factory class that has two methods one utilizes the Class generic T value and the other only uses its own method generic definitions.

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){...}
}

The method that utilizes the T generic works fine but when I want to use the other method that doesn't care what the T generic is it won't use the Generic E it will just return an Object and then I have to type cast it.

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

This has compile errors because it wants me to typecast it to (Data). If I pass the GenericClassFactory a valid Class Generic it will work. Its like it doesn't recognize method generics if you have a Class Generic defined but not used.

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

That works fine. But it's dumb that I would have to define a class generic like that when it isn't needed for my purposes. I could do this:

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){...}
}

But now my second method seems like its too broad or something...maybe not. I mean it will still give a compile error if the object you are assigning to the return type doesn't implement ClassMatchable. Is that the way I should go? So that I don't have to typecast?

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

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

发布评论

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

评论(4

风流物 2024-11-09 16:48:02

没错,如果不键入类引用,那么即使只使用方法类型参数的泛型方法也不会被泛型。这是 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 被解析为 List,而不是 List即使 Scratchpad.listT 无关。

JLS 第 4.8 节对此进行了详细记录:

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

That's right, if you don't type a class reference, then even generic methods that only use method type parameters will not be generified. It's one of the weirder nuances of Java Generics. As you say, you can put in some arbitrary type for T:

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

But more likely this shouldn't even be an instance method. Can't it be a static method? If so you could just invoke it like this:

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

Edit

Note that this extends to all instance members, not just generic instance methods. Thus, there are simpler cases that demonstrate this odd nuance. This code compiles with only warnings:

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

And that's because sp.list is resolved as a List, not a List<String>, even though Scratchpad.list has nothing to do with T.

This is verbosely documented in the JLS, Section 4.8:

The type of a constructor (§8.8), instance method (§8.8, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the erasure of its type in the generic declaration corresponding to C. The type of a static member of a raw type C is the same as its type in the generic declaration corresponding to C.

灯角 2024-11-09 16:48:02

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

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

看来 Java 无法从参数中推断出它。

当然:

就像它不识别方法一样
泛型(如果您有类泛型)
已定义但未使用。

是完全正确的。

You should tell the actual types of E and K when calling the method:

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

It appears that Java can't infer it from the argument.

And, of course:

Its like it doesn't recognize method
generics if you have a Class Generic
defined but not used.

is exactly correct.

天赋异禀 2024-11-09 16:48:02

如果您定义了类泛型但未使用它,它就无法识别方法泛型。

完全正确。如果您在类上定义通用约束,然后在不提供任何通用约束的情况下实例化该类(即,完全省略 <>),那么您就进入了原始类型领域,其中什么都没有不再一样了。

原始类型的存在只是为了向后兼容。根据 Angelika Langer 的优秀 Java 泛型常见问题解答

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

它还声明

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

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

但是,如果由于某种原因它确实必须是实例方法,您可以使用 通配符类型 GenericClassFactory:

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

Its like it doesn't recognize method generics if you have a Class Generic defined but not used.

Exactly right. If you define a generic constraint on a class, and then instantiate the class without providing any generic constraint (that is, you leave off the <> completely), then you've just stepped into the realm of Raw Types, where nothing is the same anymore.

Raw Types only exist for backwards compatibility. According to Angelika Langer's excellent Java Generics FAQ,

The use of raw types in code written after the introduction of genericity into the Java programming language is discouraged. According to the Java Language Specification, it is possible that future versions of the Java programming language will disallow the use of raw types.

It also states:

Methods or constructors of a raw type have the signature that they would have after type erasure. A method or constructor call to a raw type generates an unchecked warning if the erasure changes the argument types.

If the newObject() method doesn't make use of the type parameter T of the class that it belongs to, then something is wrong with your design: most likely newObject() should be made a static method.

However, if it really must be an instance method for some reason, you may be able to get it to work by using the wildcard type GenericClassFactory<?>:

GenericClassFactory<?> gcf = new GenericClassFactory();
Data data = gcf.newObject(new ClassMatcher<Data, String>(){...}, "key1", "my.package.name.impl"); 
蓝戈者 2024-11-09 16:48:02

考虑 T 是否确实是类或方法的类型参数。例如,类中是否有某些内容将创建的类型限制为 T(类级别),或者可能是为了避免强制转换结果值(方法级别)而使用它。

从您在这里发布的内容来看,在我看来 T 应该是该方法的类型,而您的最后一个示例就是答案。如果类实现是通用的并且可以在每次方法调用时给出不同的类型,则方法定义看起来并不太宽。

Consider if T really is a type parameter of the class or the method. For example, is there something in the class that restricts created types to T (class-level), or perhaps it is being used as a convenience to avoid casting result values (method-level).

From what you've posted here, it seems to me that T should be a type on the method and that your last example is the answer. The method definition doesn't seem too wide, if the class implementation is generic and can give out different types with each method invocation.

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