我有一个通用类工厂类,它有两种方法,一种使用类通用 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?
发布评论
评论(4)
没错,如果不键入类引用,那么即使只使用方法类型参数的泛型方法也不会被泛型。这是 Java 泛型中最奇怪的细微差别之一。正如您所说,您可以为
T
输入某种任意类型:但更可能的是,这甚至不应该是实例方法。不能是静态方法吗?如果是这样,您可以像这样调用它:
编辑
请注意,这扩展到所有实例成员,而不仅仅是通用实例方法。因此,有一些更简单的案例可以证明这种奇怪的细微差别。此代码编译时仅显示警告:
那是因为
sp.list
被解析为List
,而不是List
,即使Scratchpad.list
与T
无关。JLS 第 4.8 节对此进行了详细记录:
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
: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:
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:
And that's because
sp.list
is resolved as aList
, not aList<String>
, even thoughScratchpad.list
has nothing to do withT
.This is verbosely documented in the JLS, Section 4.8:
调用该方法时,您应该告诉 E 和 K 的实际类型:
看来 Java 无法从参数中推断出它。
当然:
是完全正确的。
You should tell the actual types of E and K when calling the method:
It appears that Java can't infer it from the argument.
And, of course:
is exactly correct.
完全正确。如果您在类上定义通用约束,然后在不提供任何通用约束的情况下实例化该类(即,完全省略
<>
),那么您就进入了原始类型领域,其中什么都没有不再一样了。原始类型的存在只是为了向后兼容。根据 Angelika Langer 的优秀 Java 泛型常见问题解答,
它还声明:
如果
newObject()
方法没有使用它所属类的类型参数T
,那么您的设计就有问题:很可能newObject()
应该成为静态方法。但是,如果由于某种原因它确实必须是实例方法,您可以使用 通配符类型
GenericClassFactory
: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,
It also states:
If the
newObject()
method doesn't make use of the type parameterT
of the class that it belongs to, then something is wrong with your design: most likelynewObject()
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<?>
:考虑
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.