Java 未检查重写返回类型

2024-03-10

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

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);
    }
}

我收到编译器警告SomeThing.getThingDoer()说的是:

未经检查的覆盖:返回类型需要未经检查的转换。

Found ThingDoer<SomeThing, String>, 必需的ThingDoer<T, String>

一切都编译得很好,虽然我没有机会测试DoThing.doIt()然而,我没有理由相信它行不通。

我的问题是,这可以打破吗?有更好的方法吗?我可以做DoThing一个基类,并且两者都有子类SomeThing and SomeOtherThing但这看起来不太优雅。

EDIT:我想避免BaseThing通用的。


我们先看看你的BaseThing您不想通用的类:

public abstract class BaseThing {
    public abstract <T extends BaseThing> ThingDoer<T, String> getThingDoer();
}

This is not一个泛型类,但它包含一个通用方法。通常,像这样的泛型方法的设计使得类型<T>由编译器根据该方法的某些参数进行绑定。例如:public <T> Class<T> classOf(T object)。但就您而言,您的方法不带任何参数。这也有些常见,在方法的实现返回一些“通用”通用(我的术语)的情况下,就像来自Collections实用类:public <T> List<T> emptyList()。该方法不带参数,但类型<T>inferred来自调用上下文;有用only因为实施emptyList()返回一个在所有情况下都是类型安全的对象。由于类型擦除,该方法实际上并不知道T当它被调用时。

现在,回到你的课堂。当您创建这些子类时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();
    }
}

在这里,您想要覆盖abstract来自基类的方法。 Java 中允许重写返回类型只要返回类型在原始方法的上下文中仍然有效。例如,您可以重写返回的方法Number具有始终返回的特定实现Integer对于该方法,因为Integer is a Number.

然而,对于泛型,List<Integer> is not a List<Number>。因此,虽然您的抽象方法被定义为返回ThingDoer<T, String>(对于一些T extends BaseThing),你的重载返回ThingDoer<SomeThing, String> and ThingDoer<SomeOtherThing, String>通常与某些未知的不兼容T虽然SomeThing and SomeOtherThing两者都延伸自BaseThing.

调用者(来自抽象 API)期望一些未知的、不可执行的T不能保证您的任何具体实现都能满足这一要求。事实上,您的具体重载不再是通用的(它们返回特定的静态绑定类型参数),并且与抽象类中的定义相冲突。

EDIT: 定义抽象方法的“正确”方法(没有警告)应该是这样的:

public abstract ThingDoer<? extends BaseThing, String> getThingDoer();

这让调用者清楚地知道它正在得到一个ThingDoer它的第一个类型参数绑定到某物延伸BaseThing(所以它可以像使用它一样使用它BaseThing)但是调用者will not通过抽象API访问时知道具体的实现。

EDIT #2- 我们在聊天中讨论的结果...

OP的原始示例用法是:

BaseThing thing = /* ... */;
thing.getThingDoer().do(thing);

注意如何相同thing引用被传递回从同一事物返回的对象中的方法getThingDoer()方法。返回的对象getThingDoer()需要与具体的实现类型紧密结合thing(根据OP)。对我来说,这闻起来就像封装破裂了。

相反,我建议将逻辑操作公开为BaseThingAPI 并将委托封装到ThingDoer作为内部实施细节。生成的 API 看起来像这样:

thing.doTheThing();

并实现有点像:

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);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 未检查重写返回类型 的相关文章

随机推荐