Java通用方法擦除和继承 [英] Java generic method erasure and inheritance

查看:98
本文介绍了Java通用方法擦除和继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到了javas泛型和覆盖方法的问题.想象一下,我有一个像树一样深的类层次结构.顶级类定义了一个方法foo,该方法带有1个类型为Strategy的参数.策略具有通用类型参数.

I am running into a problem with javas generics and overriding methods. Imagine I have a deep tree-like class hierarchy. The top-level class defines a method foo which takes 1 argument of type Strategy. Strategy has a generic type parameter.

我的类层次结构中的每个类都需要重写foo来限制可以传递的Strategy的类型,以便该Strategy的泛型类型参数与声明的类匹配.下面是一个示例:

Each class in my class hierarchy needs to override foo to limit the kind of Strategy it can be passed so that the generic type parameter of the strategy matches the declaring class. Below is an example:

abstract static class TopLevelClass {

    static final Strategy<TopLevelClass> s1 = tlc -> System.out.println(tlc.getTopLevelAtt());

    String getTopLevelAtt() {
        return "TopLevelAtt";
    }

    void foo(Strategy<TopLevelClass> s) {s.bar(this);}
}
static class MidLevelClass extends TopLevelClass {

    static final Strategy<MidLevelClass> s2 = mlc -> System.out.println(mlc.getMidLevelAtt());

    String getMidLevelAtt() {
        return "MidLevelAtt";
    }

    void foo(Strategy<MidLevelClass> s) {s.bar(this);}
}
static class LowLevelClass extends MidLevelClass  {

    static final Strategy<LowLevelClass> s3 = llc -> System.out.println(llc.getTopLevelAtt());

    String getLowLevelAtt() {
        return "LowLevelAtt";
    }

    void foo(Strategy<LowLevelClass> s) {s.bar(this);}
}
static interface Strategy<X> {
    void bar(X x);
}

在此示例中,我希望能够使用分别在TopLevelClass,MidLevelClass和LowLevelClass中定义的静态引用s1,s2和s3中的任何一个,在LowLevelClass类的实例上调用foo.理想情况下,根据参数,我不必调用不同的方法foo1,foo2或foo3.

In this example I want to be able to call foo on instances of class LowLevelClass with any of the static references s1, s2 and s3 defined in TopLevelClass, MidLevelClass and LowLevelClass respectively. Ideally I would not have to call different methods foo1, foo2 or foo3 depending on the argument.

上面的代码 NOT 用Java编译.编译时错误为:

The code above does NOT compile in java. The compile-time-error is:

名称冲突:MidLevelClass类型的foo(Strategy)方法具有与TopLevelClass类型的foo(Strategy)相同的擦除,但不会覆盖

Name clash: The method foo(Strategy) of type MidLevelClass has the same erasure as foo(Strategy) of type TopLevelClass but does not override it

我怀疑这很容易解决.我可以只使用原始类型并依靠运行时类型检查,但是我宁愿保持类型安全.在不牺牲类型层次结构或类型安全性的前提下,我该怎么做?请注意,在构造函数中传递策略 IS NOT 对我来说是一个选择!在对象的生存期内必须多次调用foo.

I doubt this can easily be resolved. I could just use raw-types and rely on run-time typechecks but I would rather keep type safety. What can I do to achieve this without sacrificing the type hierarchy or type safety? Please note that passing the Strategy in the constructor IS NOT an option for me! It must be possible to call foo multiple times over the life time of the object.

我意识到,如果不了解周围的情况,可能很难解决这个问题.我在这里打开了一个更详细的问题,解释了我的问题的背景:如何使此策略-对象模式类型安全

I realize, that this problem is perhaps difficult to follow without knowing the circumstances surrounding it. I have opened a more detailed question explaining the background of my problem here: How to make this Strategy-Object pattern type safe

推荐答案

如果您担心删除操作,请为单独的方法使用单独的方法名称:

If you are worried about erasure then just use separate method names for separate methods:

abstract class TopLevelClass {
    void fooTop(Strategy<TopLevelClass> s) {/*...*/}
}
class MidLevelClass extends TopLevelClass {
    void fooMid(Strategy<MidLevelClass> s) {/*...*/}
}
class LowLevelClass extends MidLevelClass  {
    void fooLow(Strategy<LowLevelClass> s) {/*...*/}
}

但是,我怀疑擦除不是您的问题.您大概想重写相同的方法.

However, I suspect erasure is not your problem. You presumably want to override the same method.

Strategy<LowLevelClass>的实例不可能是Strategy<MidLevelClass>,也不能是策略;

An instance of Strategy<LowLevelClass> cannot possibly be a Strategy<MidLevelClass>, which cannot be a Strategy;

给予

Strategy<LowLevelClass> l;
Strategy<MidLevelClass> m;

那么您就无法将一个分配给另一个.

Then you cannot assign one to another.

l = m; // Compile-time fail.
m = l; // Compile-time fail.

因此,能够通过方法重写执行相同操作是没有意义的. (尽管从1.5开始就有协变量的返回类型,但bar(TopLevelClass)不能覆盖bar(MidLevelClass)总是很正确的.)

And therefore it would make no sense to be able to do the same via method overriding. (It's also always been true that bar(TopLevelClass) cannot override bar(MidLevelClass), though since 1.5 there are covariant return types.)

将类型参数添加到类中,以用作方法中的类型参数.

Add a type parameter to the class to use as a type argument in the method.

abstract class TopLevelClass<T extends TopLevelClass<T>> {
    void foo(Strategy<T> s) {/*...*/}
}
class MidLevelClass<T extends MidLevelClass<T>> extends TopLevelClass<T> {
    void foo(Strategy<T> s) {/*...*/}
}
class LowLevelClass<T extends LowLevelClass<T>> extends MidLevelClass<T>  {
    void foo(Strategy<T> s) {/*...*/}
}

更新后的问题将this的使用添加为对Strategy.foo的调用的参数.这意味着MidLevelClass必须为abstract-它不能保证foo被覆盖. this的类型现在需要适合type参数.为此,添加abstract "getThis" 方法(具体子类中具体).

The updated question add the use of this as a argument to a call of Strategy.foo. This implies MidLevelClass must be abstract - it cannot guarantee foo is overridden. The type of this now needs to fit the type parameter. To do that, add an abstract "getThis" method (concrete in concrete subclasses).

    protected abstract X getThis();
...
    @Override protected X getThis() { return this; }

static字段的类型需要通配符:

The type of the static fields requires wildcards:

static final Strategy<? extends TopLevelClass<?>> s1 =
    tlc -> System.out.println(tlc.getTopLevelAtt());

(更好的设计更倾向于使用组合而不是继承.)

这篇关于Java通用方法擦除和继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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