当前位置: 首页 > 知识库问答 >
问题:

使用泛型参数重写泛型方法时发生Java编译错误

林礼骞
2023-03-14
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(集合)必须重写或实现超类型方法

    只有带有泛型的参数才会出现此错误<代码>get3编译,但当然有一个警告:

    >

    显然还有其他方法可以解决这个问题,但在我的理解中,这应该是一个法律优先事项,我的问题更多的是为什么会有这个编译错误。提前谢谢!

    编辑:

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

    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的明显类型安全警告,我也希望在get2上获得。
    • 我希望每个方法都有自己的类型,所以类类型不是解决方案。
    • 在这个例子中使用Object是一个错误的决定,但这不是我的观点,旧get3的类型隐藏问题如上所述很清楚。
    • 问题就出在签名上,就像lancore说的,所以我要评论一下他的回答。
  • 共有3个答案

    汤飞
    2023-03-14

    之所以B.get2()没有覆盖A.get2()是因为根据JLS,这两个类型参数不相同。。。

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

    >

    其中 n 是n的类型参数,设θ=[B1:=A1,…,Bn:=An]。然后,对于所有<代码>i(1≤ 我≤ n) ,Ai的界与应用于Bi的界的θ的类型相同。

    A. get2()具有公共摘要

    为了使JLS具有合法的覆盖情况,两种方法必须是JLS所称的覆盖等效方法。。。

    ...两个方法签名M1M2是覆盖等效的,如果M1M2的子签名或M2M1的子签名...

    除非两个方法中的一个是另一个的子签名,否则这两个方法不能重写为等价的。。。

    ...

    方法签名是方法签名的子签名,如果存在以下任一情况:。。。

    1. M的签名与M的签名相同
    2. M的签名与M的签名的擦除(§4.6)相同

    ...

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

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

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

    ...

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

    1. 它们具有相同数量的形式参数(可能为零)
    2. 它们具有相同数量的类型参数(可能为零)
    3. A1,..., AnM的类型参数,设B1,..., BnN的类型参数。将N中的Bi的每次出现重命名为Ai后,对应类型变量的界限相同,MN的形式参数类型相同。

    ...

    因此,对于编译的B.get2(),以及对于100%类型安全的B.get(),您需要将方法更改为重写等效方法。

    最简单的方法是使它们具有与它们覆盖的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;
            }
        }
    }
    

    赵智
    2023-03-14

    正如Ghost Cat已经提到的,get3中的Object实际上是一个类型变量。有趣的是,您应该使用什么,但名称令人困惑。

    要修复内部类,必须为所有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;
        }
    }
    

    然而,所有这些重复的<代码>

    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属于所有Objects(从您的示例中看不清楚),您可以将上面的内部类替换为:

    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;
        }
    }
    
    上官和韵
    2023-03-14

    在这里:

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

    可能不是你的意思。

    您正在这里引入另一个类型变量,称为Object

    将其与:

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

    换句话说:get2()使用java.lang.对象get3()使用一个泛型类型,不幸的是,它被命名为Object。你也可以写

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

    最后:get2()也不是您要做的。请参阅迈克尔关于该部分的精彩回答。

     类似资料:
    • 安吉丽卡·兰格(Angelica Langer)在关于仿制药的常见问题解答中说(参见Technicalities.FAQ822): 如果这些方法具有具有不同边界的类型参数,则它们不会重写,因为这些方法的签名不是重写等价的。请记住,类型参数边界是泛型方法签名的一部分。 示例(泛型子类型方法重载泛型超类型方法;不推荐): 我不明白为什么方法在类中重载。据我所知,这应该是一个编译时错误,因为在和中具有相

    • 我试图覆盖add(),但它不会编译 错误消息 java:36:错误:名称冲突:AVLTree中的add(E#1)和BinaryTree中的add(E#2)具有相同的擦除,但两者都不重写另一个公共void add(E toInsert)^,其中E#1、E#2是类型变量:E#1扩展了在类AVLTree中声明的Comparable E#2扩展了在类BinaryTree中声明的Comparable

    • 我正在尝试覆盖子类中从父类继承抽象方法的方法。我正在尝试更改泛型返回类型,但我有点困惑。 基本泛型类: 儿童班: 实施: 我想在其中覆盖参数化方法的子视图类: 这是Eclipse抛出错误消息的地方: 注意类和接口:事件和级别: 以及继承自 的 Model 类 我试图实现的是编写更抽象的代码,因为这些类、接口将被多个类扩展(在我的例子中是不同的视图类型)。这些是抽象类:< code>BaseAdap

    • 来自Java文档:在类C中声明的实例方法m1覆盖在类A中声明的另一个实例方法m2,如果以下所有条件都为真:。。。。。m1的签名是m2签名的子签名(§8.4.2)。 方法m1的签名是方法m2签名的子签名,前提是:m1的签名与m2签名的擦除相同(§4.6)。 是对一个 如果是这样,什么是错的? EDIT1:或者如果我切换通用类型: 这种情况下有什么问题? EDIT2:我知道如何修复它,但我想了解,在这

    • 这是如何编译的: 但这并不是: 它给我以下编译错误: Impl不是抽象的,并且不会覆盖测试中的抽象方法getValue(整数) 错误:名称冲突:Impl中的getValue(T)和测试中的getValue(Integer)具有相同的擦除,但两者都不重写另一个 擦除不能确保t被整数替换吗?那么为什么第二个例子是无效的呢?

    • 我正在做一个体验,尝试用以下方式重写泛型类的方法: 为什么我不能?编译错误是 yGenFun。java:15:错误:对doX的引用不明确,Do中的方法doX(A)和MyGenFun中的方法doX(T)都匹配x.doX(“测试”);^其中A、T是类型变量:在类Do T中声明的extends对象扩展在类MyGenFun 1 error中声明的CharSequence 如果我注释“第1行”,我可以编译代