Java未检查覆盖返回类型 [英] Java Unchecked Overriding Return Type

查看:1674
本文介绍了Java未检查覆盖返回类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包含以下组件的项目:

  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()的实现返回一个在所有情况下都是类型安全的对象。由于类型擦除,该方法实际上在调用 T 时并不知道它的类型。



现在回到你的课堂。当您创建 BaseThing 的这些子类时:

  public class SomeThing extends BaseThing {
public ThingDoer< SomeThing,String> getThingDoer(){
返回Things.getSomeThingDoer();
}
}

公共类SomeOtherThing扩展BaseThing {
public ThingDoer< SomeOtherThing,String> getThingDoer(){
返回Things.getSomeOtherThingDoer();




$ b $ p
$ b

在这里,你想覆盖<$ c $从基类中抽取方法。只要返回类型在原始方法的上下文中仍然有效,就允许在Java 中覆盖返回类型。例如,你可以重写一个返回 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>, required ThingDoer<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 both SomeThing and SomeOtherThing 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 the Collections 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 of emptyList() returns an object that is type-safe in all cases. Due to type erasure, the method doesn't ever actually know the type of T 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 returns Number with a specific implementation that always returns Integer for that method, because Integer is a Number.

With generics, however, a List<Integer> is not a List<Number>. So while your abstract method is defined to return ThingDoer<T, String> (for some T extends BaseThing), your overloads that return ThingDoer<SomeThing, String> and ThingDoer<SomeOtherThing, String> are not generally compatible with some unknown T even though SomeThing and SomeOtherThing both extend from BaseThing.

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 extends BaseThing (so it can use it as if it were a BaseThing) 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's getThingDoer() method. The object returned by getThingDoer() needs to be tightly bound to the concrete implementation type of thing (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 the ThingDoer 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆