Java未检查覆盖返回类型 [英] Java Unchecked Overriding Return Type
问题描述
我有一个包含以下组件的项目:
public abstract class BaseThing {
public abstract< T扩展了BaseThing> ThingDoer< T,String> getThingDoer();
}
公共类SomeThing扩展BaseThing {
public ThingDoer< SomeThing,String> getThingDoer(){
返回Things.getSomeThingDoer();
}
}
公共类SomeOtherThing扩展BaseThing {
public ThingDoer< SomeOtherThing,String> getThingDoer(){
返回Things.getSomeOtherThingDoer();
}
}
public class Things {
public ThingDoer< SomeThing,String> getSomeThingDoer {
return getThingDoer(SomeThing.class);
}
public ThingDoer< SomeOtherThing,String> getSomeOtherThingDoer {
return getThingDoer(SomeOtherThing.class);
}
private< D extends ThingDoer< T,String> D getThingDoer(Class< T> clazz){
// get ThingDoer
}
}
公共类ThingDoer< T,V> {
public void do(T thing){
// do thing
}
}
public class DoThing {
private BaseThing thing ;
public void doIt(){
thing.getThingDoer()。do(thing);
$ / code>
我在 SomeThing.getThingDoer()
表示:
未检查覆盖:返回类型需要未经检查的转换。
找到
ThingDoer< SomeThing,String>
,要求ThingDoer< T,String>
Everthing编译得很好,虽然我没有机会测试 DoThing.doIt()
然而,我没有理由相信它不会起作用。
我的问题是,这个问题可以解决吗?有没有更好的方法来做到这一点?我可以使> DoThing
一个基类,并为 SomeThing
和 SomeOtherThing
但这看起来不太优雅。
编辑:我想避免让 BaseThing
generic。
让我们先看看 BaseThing
类,你不想做通用的:
public abstract class BaseThing {
public abstract < T扩展BaseThing> ThingDoer< T,String> getThingDoer();
这是不是泛型类,但它包含一个泛型方法。通常,像这样的通用方法被设计为使得类型< T>
被编译器基于方法的一些参数绑定。例如: public< T>类< T> classOf(T object)
。但在你的情况下,你的方法没有任何参数。如果方法的实现从 Collections
实用程序类返回某些通用通用(我的术语),如下所示: public< T>列表与LT; T>的emptyList()
。这个方法不带任何参数,但类型< T>
将从调用上下文中推断;它仅适用于 ,因为 emptyList()
的实现返回一个在所有情况下都是类型安全的对象。由于类型擦除,该方法实际上在调用
现在回到你的课堂。当您创建 BaseThing
的这些子类时:
public class SomeThing extends BaseThing {
方法。只要返回类型在原始方法的上下文中仍然有效,就允许在Java 中覆盖返回类型。例如,你可以重写一个返回
public ThingDoer< SomeThing,String> getThingDoer(){
返回Things.getSomeThingDoer();
}
}
公共类SomeOtherThing扩展BaseThing {
public ThingDoer< SomeOtherThing,String> getThingDoer(){
返回Things.getSomeOtherThingDoer();
$ b $ p
$ b在这里,你想覆盖<$ c $从基类中抽取
Number
的方法,该方法总是返回Integer
,因为整数
是一个数字
。
<然而,对于泛型而言,List< Integer>
不是< code> List< Number> C>。所以当你的抽象方法被定义为返回
- 我们在聊天中讨论的发现...ThingDoer< T,String>
(对于某些T扩展BaseThing
),重载ThingDoer< SomeThing,String>
和ThingDoer< SomeOtherThing,String>
code> T 即使SomeThing
和SomeOtherThing
都从BaseThing
。
调用者(来自抽象API)需要一些未知的,不可执行的
T
不能保证满足您的具体实现。实际上,您的具体重载不再是泛型的(它们返回特定的静态绑定类型参数),并且与抽象类中的定义相冲突。
编辑:
定义抽象方法的正确方式(无警告)应该是这样的:
public abstract ThingDoer <?扩展BaseThing,String> getThingDoer();
这使调用者清楚它得到
ThingDoer
,其第一个类型参数绑定到 ,它扩展了BaseThing
(所以它可以像使用BaseThing
),但调用者在抽象API访问时不会知道具体的实现。
<编辑#2
OP的原始示例用法为:
BaseThing thing = / * ... * /;
thing.getThingDoer()。do(thing);
请注意,如何传递相同的
thing
参考回到从同一事物的getThingDoer()
方法返回的对象中的方法。由getThingDoer()
返回的对象需要与具体实现类型thing
紧密绑定(根据OP )。对我来说,这听起来像破封装。
相反,我建议将逻辑操作公开为
BaseThing
API并将委托封装为ThingDoer
作为内部实现细节。生成的API看起来像这样:
thing.doTheThing();
实施方式有点像:
public class SomeThing extends BaseThing {
@Override public void doTheThing(){
Things.getSomeThingDoer()。do(this);
$ b public class SomeOtherThing extends BaseThing {
@Override public void doTheThing(){
Things.getSomeOtherThingDoer()。do(this) ;
}
}
I have a project that has the following components:
public abstract class BaseThing { public abstract <T extends BaseThing> ThingDoer<T, String> getThingDoer(); } public class SomeThing extends BaseThing { public ThingDoer<SomeThing, String> getThingDoer() { return Things.getSomeThingDoer(); } } public class SomeOtherThing extends BaseThing { public ThingDoer<SomeOtherThing, String> getThingDoer() { return Things.getSomeOtherThingDoer(); } } public class Things { public ThingDoer<SomeThing, String> getSomeThingDoer { return getThingDoer(SomeThing.class); } public ThingDoer<SomeOtherThing, String> getSomeOtherThingDoer { return getThingDoer(SomeOtherThing.class); } private <D extends ThingDoer<T, String> D getThingDoer(Class<T> clazz) { //get ThingDoer } } public class ThingDoer<T, V> { public void do(T thing) { //do thing } } public class DoThing { private BaseThing thing; public void doIt() { thing.getThingDoer().do(thing); } }
I'm getting a compiler warning in
SomeThing.getThingDoer()
that says:Unchecked overriding: return type requires unchecked conversion.
Found
ThingDoer<SomeThing, String>
, requiredThingDoer<T, String>
Everthing compiles fine, and while I didn't get a chance to test out
DoThing.doIt()
yet, I have no reason to believe that it won't work.My question is, can this break and is there a better way to do this? I could make
DoThing
a base class, and have subclasses for bothSomeThing
andSomeOtherThing
but that doesn't seem very elegant.EDIT: I would like to avoid making
BaseThing
generic.解决方案Let's look first at your
BaseThing
class that you don't want to make generic:public abstract class BaseThing { public abstract <T extends BaseThing> ThingDoer<T, String> getThingDoer(); }
This is not a generic class, but it contains a generic method. Frequently, generic methods like this are designed so that the type
<T>
is bound by the compiler based on some argument to the method. For example:public <T> Class<T> classOf(T object)
. But in your case, your method takes no arguments. That is also somewhat common, in cases where the implementation of the method returns something "universally" generic (my term) like this method from theCollections
utility class:public <T> List<T> emptyList()
. This method takes no arguments, but the type<T>
will be inferred from the calling context; it works only because the implementation ofemptyList()
returns an object that is type-safe in all cases. Due to type erasure, the method doesn't ever actually know the type ofT
when it's called.Now, back to your classes. When you create these subclasses of
BaseThing
:public class SomeThing extends BaseThing { public ThingDoer<SomeThing, String> getThingDoer() { return Things.getSomeThingDoer(); } } public class SomeOtherThing extends BaseThing { public ThingDoer<SomeOtherThing, String> getThingDoer() { return Things.getSomeOtherThingDoer(); } }
Here, you want to override the
abstract
method from the base class. Overriding the return type is allowed in Java as long as the return type is still valid in the context of the original method. For instance you can override a method that returnsNumber
with a specific implementation that always returnsInteger
for that method, becauseInteger
is aNumber
.With generics, however, a
List<Integer>
is not aList<Number>
. So while your abstract method is defined to returnThingDoer<T, String>
(for someT extends BaseThing
), your overloads that returnThingDoer<SomeThing, String>
andThingDoer<SomeOtherThing, String>
are not generally compatible with some unknownT
even thoughSomeThing
andSomeOtherThing
both extend fromBaseThing
.The caller (from the abstract API) expects some unknown, unenforceable
T
that cannot be guaranteed to be satisfied by either of your concrete implementations. In fact, your concrete overloads are no longer generic (they return specific, statically-bound type parameters) and that conflicts with the definition in the abstract class.EDIT: The "correct" way (no warnings) to define the abstract method should be something like:
public abstract ThingDoer<? extends BaseThing, String> getThingDoer();
This makes it clear to the caller that it's getting a
ThingDoer
with its first type parameter bound to something that extendsBaseThing
(so it can use it as if it were aBaseThing
) but the caller will not know the specific implementation when accessed by the abstract API.EDIT #2 - Findings from our discussion in chat...
The OP's original example usage is:
BaseThing thing = /* ... */; thing.getThingDoer().do(thing);
Notice how the same
thing
reference is passed back into a method in the object returned from that same thing'sgetThingDoer()
method. The object returned bygetThingDoer()
needs to be tightly bound to the concrete implementation type ofthing
(according to the OP). To me, this smells like broken encapsulation.Instead, I suggest exposing the logical operation as a part of the
BaseThing
API and encapsulating the delegation to theThingDoer
as an internal implementation detail. The resulting API would look something like:thing.doTheThing();
And implemented somewhat like:
public class SomeThing extends BaseThing { @Override public void doTheThing() { Things.getSomeThingDoer().do(this); } } public class SomeOtherThing extends BaseThing { @Override public void doTheThing() { Things.getSomeOtherThingDoer().do(this); } }
这篇关于Java未检查覆盖返回类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!