繁体   English   中英

Java编译错误用泛型参数覆盖泛型方法

[英]Java compile error overriding generic method with generic parameter

import java.util.Collection;

public abstract class A {

    public class R<T> { }

    public abstract <X> R<X> get1();

    public abstract <X> R<X> get2(Collection<String> p);

    public abstract <X> R<X> get3(Collection<Integer> p);

    public class B extends A {

        @Override
        public R<Object> get1() {
            return null;
        }

        @Override
        public R<Object> get2(Collection<String> p) {
            return null;
        }   

        @Override
        public <Object> R<Object> get3(Collection<Integer> p) {
            return null;
        }
    }
}

get1方法工作正常,但get2存在编译错误:

  • AB 类型的方法 get2(Collection) 必须覆盖或实现超类型方法

此错误仅出现在带有泛型的参数中。 get3编译但当然有警告:

  • 类型参数 Object 隐藏了类型 Object

显然还有其他方法可以解决这个问题,但在我看来,这应该是一个合法的覆盖,我的问题更多的是为什么会出现这个编译错误。 提前致谢!

编辑:

对不起,我的例子不够清楚。 因此,这里有一个新的回应你的答案的一些观点。

public abstract class A {

    public abstract class R<T> { }

    public abstract <X> R<X> get();

    public abstract <Y> R<Y> get2(Collection<String> p);

    public abstract <Z> R<Z> get3(Collection<Integer> p);

    public class B extends A {

        @Override
        public R<String> get() {
            return null;
        }

        @Override
        public R<Double> get2(Collection<String> p) {
            return null;
        }   

        @Override
        public <V> R<V> get3(Collection<Integer> p) {
            return null;
        }
    }
}
  • 与上述get2相同的编译错误,以及我希望在get2get的明显类型安全警告。
  • 我希望每个方法都有自己的类型,因此类类型不是解决方案。
  • 在这个例子中使用 Object 是一个错误的决定,但这不是我的观点,旧的get3的类型隐藏问题很明显,如上所述。
  • 正如 lexicore 所说,问题在于签名。 因此,我将评论他的回答。

这里:

public <Object> R<Object> get3(Collection<Integer> p) {

可能不是你的意思。

您正在引入另一种类型变量,此处称为Object

比较一下:

public R<Object> get2(Collection<String> p) {

换句话说: get2()使用 java.lang.Object。 get3()使用一个不幸的泛型类型Object 你也可以写

public <NONSENSE> R<NONSENSE> get3(Collection<Integer> p) {

最后: get2()也不是您要做的。 请参阅 Michael 对那部分的出色回答。

正如GhostCat已经提到的Objectget3实际上是一个类型的变量。 也就是说,有趣的是,您应该使用什么,但名称令人困惑。

要修复您的内部类,您必须再次为所有 3 个方法重新声明泛型类型参数:

public class B extends A
{
    @Override
    public <X> R<X> get1() {
        return null;
    }

    @Override
    public <X> R<X> get2(Collection<String> p) {
        return null;
    }

    @Override
    public <X> R<X> get3(Collection<Integer> p) {
        return null;
    }
}

但是, <X>所有重复都表明您应该将泛型类型参数移动到类A 这是我实现这一点的方式:

abstract class A<X>
{
    public class R<T> { }

    public abstract R<X> get1();

    public abstract R<X> get2(Collection<String> p);

    public abstract R<X> get3(Collection<Integer> p);

    public class B extends A<X>
    {
        @Override
        public R<X> get1() {
            return null;
        }

        @Override
        public R<X> get2(Collection<String> p) {
            return null;
        }

        @Override
        public R<X> get3(Collection<Integer> p) {
            return null;
        }
    }
}

如果B属于所有Object s(从您的示例中不清楚),您可以将上面的内部类替换为:

public class B extends A<Object>
{
    @Override
    public R<Object> get1() {
        return null;
    }

    @Override
    public R<Object> get2(Collection<String> p) {
        return null;
    }

    @Override
    public R<Object> get3(Collection<Integer> p) {
        return null;
    }
}

B.get2()不覆盖A.get2()的原因是因为根据 JLS两者没有相同的类型参数......

...如果以下两项都为真,则两个方法或构造函数M 1M 2具有相同的类型参数: ...

  1. M 1M 2具有相同数量的类型参数(可能为零)。

  2. 其中A 1 , ..., AnM的类型参数, B 1 , ..., B nN的类型参数,令θ=[B 1 :=A 1 , ..., B n :=A n ] 然后,对于所有i (1 ≤ i ≤ n)A i的边界与应用于B i的边界的θ类型相同。

A.get2()具有public abstract <X> 类型参数部分 R<Double>public R<Double> B.get2()不在该特定上下文类型参数 在这种情况下, DoubleA同名泛型方法的调用实例化)中的类型参数

为了有 JLS 合法的覆盖情况,两种方法必须JLS 称为覆盖等效的方法......

...两个方法签名M 1M 2是覆盖等效当且仅当任一M 1是一个子签名M 2M 2是一个子签名M 1 ...

除非两个方法之一是另一个的子签名,否则两个方法不能是覆盖等效的...

...

方法M 1的签名是方法M 2签名的子签名,如果: ...

  1. M 2具有与M 1相同的签名,或
  2. M 1的签名与M 2的签名的擦除(第 4.6 节)相同。

...

两个方法不能是彼此的子签名,除非它们具有相同的签名......

... 如果两个方法具有相同的名称和参数类型,则它们具有相同的签名...

您的两个方法没有相同的签名,因为它们不满足JLS的相同参数类型条件......

...

如果以下所有条件都成立,则两个方法或构造函数声明MN具有相同的参数类型:

  1. 它们具有相同数量的形式参数(可能为零)
  2. 它们具有相同数量的类型参数(可能为零)
  3. A 1 , ..., A nM的类型参数,让B 1 , ..., B nN的类型参数。 N类型中每次出现的 a B i重命名为A i ,对应的类型变量的边界相同, MN形参类型相同。

...

因此,为了让B.get2()编译——并且B.get()是 100% 类型安全的——那么你需要将方法更改为覆盖等效。

最简单的方法是使它们具有与它们覆盖的A中相同数量的类型参数......

import java.util.Collection;

public abstract class A {

    public abstract class R<T> {

    public abstract <X> R<X> get();

    public abstract <Y> R<Y> get2(Collection<String> p);

    public abstract <Z> R<Z> get3(Collection<Integer> p);

    public static class B extends A {

        @Override
        public <T> R<T> get() {
            return null;
        }

        @Override
        public <U> R<U> get2(Collection<String> p) {
            return null;
        }

        @Override
        public <V> R<V> get3(Collection<Integer> p) {
            return null;
        }
    }
}

尝试

...
public <X> R<X> get2(Collection<String> p){
...
public <X> R<X> get3(Collection<Integer> p) {
...

您的X泛型类型不是 and Object ,而是扩展Object东西,因为用Object替换Object确实没有意义。 get2 方法的有效原型是这样的:

// X or Y or any generic type or class
public <Y> R<Y> get2(Collection<String> p) {
    return null;
}

与你的 get3 方法一样,你不需要泛型来操作对象,它应该是扩展Object东西

public <Y> R<Y> get3(Collection<Integer> p) {
     return null;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM