添加参数

时间:2018-05-11 16:56:13

标签: java generics override method-overriding

我想知道为什么这是一个有效的覆盖:

public abstract class A {

    public abstract <X> Supplier<X> getSupplier();

    public static class B extends A {

        @Override
        public Supplier<String> getSupplier() {
            return String::new;
        }
    }
}

而不是:

public abstract class A {

    public abstract <X> Supplier<X> getSuppliers(Collection<String> strings);

    public static class B extends A {

        @Override
        public Supplier<String> getSuppliers(Collection<String> strings) {
            return String::new;
        }
    }
}

根据JLS §8.4.8.1B.getSupplier必须是子签名A.getSupplier

  

在C类中声明或继承的实例方法mC,覆盖C类中声明的另一个方法mA,iff以下所有条件都为真:

     
      
  • ...
  •   
  • mC的签名是mA签名的子签名(§8.4.2)。
  •   
  • ...
  •   

子签名在JLS §8.4.2中定义:

  

两个方法或构造函数M和N具有相同的签名,如果它们具有相同的名称,相同的类型参数(如果有的话)(第8.4.4节),并且在调整之后形式参数类型N为M的类型参数,形式参数类型相同。

     

方法m1的签名是方法m2的签名的子签名,如果:

  • m2与m1具有相同的签名,或
  • m1的签名与m2签名的删除(§4.6)相同。

因此,似乎B.getSupplierA.getSupplier的子签名,但B.getSuppliers 不是 A.getSuppliers的子签名。

我想知道情况如何。

如果B.getSupplierA.getSupplier的子签名,因为它具有相同的删除,那么B.getSuppliers也必须与A.getSuppliers具有相同的删除权。这应该足以覆盖getSuppliers合法 - 但事实并非如此。

如果B.getSupplierA.getSupplier的子签名,因为它具有相同的签名,那么我想知道“相同的类型参数(如果有的话)”究竟意味着什么。

如果考虑类型参数,则它们应具有不同的类型参数:A.getSupplier具有类型参数XB.getSupplier没有。
如果不考虑类型参数那么getSuppliers如何不同?

这更像是关于覆盖和泛型的学术问题所以请不要建议重构代码(比如将类型参数X移动到类等)。

我正在寻找基于JLS的正式答案。

从我的观点来看,B.getSupplier不应该覆盖A.getSupplier,因为它们没有相同的类型参数。这使得以下代码(生成ClassCastException)合法:

A b = new B();
URL url = b.<URL>getSupplier().get();

2 个答案:

答案 0 :(得分:5)

根据编译器输出,两个示例中的方法签名都不同(使用-Xlint:unchecked选项编译代码以确认它):

<X>getSupplier() in A (m2)
                                 1st snippet
getSupplier()    in B (m1)


<X>getSuppliers(Collection<String> strings) in A (m2)
                                                           2nd snippet
getSuppliers(Collection<String> strings)    in B (m1)

根据JLS规范,方法m 1 的签名是方法m 2的签名的子签名 if:

  
      
  • m 2 与m 1

  • 具有相同的签名   
  • m 1 的签名与m 2 的签名擦除相同。

  •   

第一个声明不在游戏中 - 方法签名不同。但是第二个声明和删除呢?

有效覆盖

B.getSupplier()(m 1 )是A.<X>getSupplier()(m 2 )的子签名,因为:

  
      
  • m 1 的签名与m 2 的签名擦除相同
  •   
擦除后

<X>getSupplier()等于getSupplier()

无效覆盖

B.getSuppliers(...)(m 1 )不是A.<X>getSuppliers(...)(m 2 )的子签名,因为:

  
      
  • m 1 的签名与m 2
  • 签名的删除不同   

m 1 的签名:

getSuppliers(Collection<String> strings);

擦除m 2 的签名:

getSuppliers(Collection strings);

将m 1 参数从Collection<String>更改为原始Collection消除了错误,在这种情况下,m 1 成为m <的子签名子> 2

结论

第一个代码段(有效覆盖):父类和子类中的方法签名最初是不同的。但是,在将擦除应用于父方法之后,签名变得相同。

第二个代码段(无效覆盖):方法签名最初不同,并且在将擦除应用于父方法后仍然不同。

答案 1 :(得分:1)

当你添加参数时,它不再是一个覆盖并成为一个过载。

泛型与它无关。