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

时间:2018-05-11 13:28:13

标签: java generics override

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存在编译错误:

  •   

    A.B类型的方法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相同的编译错误,以及get的{​​{1}}明显的类型安全警告,我希望它也会出现在get2上。
  • 我希望每个方法都有自己的类型,因此类型不是解决方案。
  • 使用Object对于此示例来说是一个错误的决定,但这不是我的观点,旧的get3的类型隐藏问题很明显,如上所述。
  • 问题在于签名,正如词汇所说。因此,我将评论他的答案。

5 个答案:

答案 0 :(得分:5)

下面:

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的优秀答案。

答案 1 :(得分:2)

As GhostCat already mentionedObject中的get3实际上是一个类型变量。也就是说,有趣的是,你应该使用什么,但名字却令人困惑。

要修复内部类,必须再次为所有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相关(从您的示例中不清楚),您可以将上面的内部类替换为:

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

答案 2 :(得分:2)

B.get2() 不覆盖 A.get2() 的原因是因为这两个类型的 参数 according to the JLS ...

... 如果以下两个条件均成立,则两个方法或构造器 M1 M2 具有相同的类型参数: ...

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

  2. 其中A1, ..., AnM的类型参数,而B1, ..., BnN的类型参数,让θ=[B1:=A1, ..., Bn:=An]。然后,对于所有i (1 ≤ i ≤ n)Ai的边界与应用于θ边界的Bi的类型相同。

A.get2() 具有 public abstract <X> type parameter section。但是在特定上下文中, R<Double> 中的public R<Double> B.get2()不是 类型参数 。在这种情况下,Double 调用 实例化)中的 类型参数 )中的同名通用方法。

要想获得JLS法律覆盖的情况,请使用两种方法 必须 what the JLS calls override-equivalent ...

... 两个方法签名 A M1 是等效的,前提是 {{1} } M2 的子签名或 M1 M2 的子签名 ...

除非两个方法之一是另一个方法的subsignature,否则两个方法不能重写等效。

...

方法 M2 的签名是方法 M1 的签名的子签名: ...

  1. M1 M2
  2. 具有相同的签名
  3. M2 的签名与 M1 签名的擦除(第4.6节)相同。

...

两个方法不能互相成为子签名,除非它们具有相同的签名...

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

您的两个方法没有相同的签名,因为它们不符合 the same argument types condition of the JLS ...

...

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

  1. 它们具有相同数量的形式参数(可能为零)
  2. 它们具有相同数量的类型参数(可能为零)
  3. M N的类型参数,让 A1 , ..., An 为{{ 1}}。将M类型的每个 B1, ..., Bn 重命名为 N 后,对应类型变量的界限相同,并且BiN的形式参数类型相同。

...

因此,要编译 Ai 并使M是100%类型安全的 –那么您需要将方法更改为等效。

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

B.get2()

答案 3 :(得分:0)

尝试

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

答案 4 :(得分:0)

您的X泛型类型不是Object,而是扩展 Object,因为替换{{ {1}} Object。 get2方法的有效原型是这样的:

Object

与你的get3方法相同,你不需要泛型来操纵对象,它应该是扩展// X or Y or any generic type or class public <Y> R<Y> get2(Collection<String> p) { return null; } 的东西

Object