当前位置: 首页 > 面试题库 >

处理同音异义继承方法的规则是什么?

拓拔坚
2023-03-14
问题内容

我试图了解Java如何处理具体类从不同的类/接口继承具有相同名称的方法(抽象或具体)时出现的歧义情况。

我一直找不到通用的规则,这就是为什么我决定一次用一种实际的方法为此花一些时间的原因。

我考虑了8种不同的情况,

  • 抽象方法
  • 非抽象方法
  • 抽象类
  • 介面

导致此方案:

                           +-------------------------+
                           |       INTERFACE         |
                           +----------+--------------|
                           | abstract | non-abstract |
                           | method   | method       |
+-----------+--------------+----------+--------------+
|           | abstract     |          |              |
| ABSTRACT  | method       |    1a    |      2a      |
|           +--------------+----------+--------------+
|   CLASS   | non-abstract |          |              |
|           | method       |    3a    |      4a      |
+-----------+--------------+----------+--------------+
|           | abstract     |          |              |
|           | method       |    1b    |      2b      |
| INTERFACE +--------------+----------+--------------+
|           | non-abstract |          |              |
|           | method       |    3b    |      4b      |
+-----------+--------------+----------+--------------+

在这里,每种情况都得到实施和评论:

// (1a) 
// A - abstract method  
// I - abstract method
//
// Implementation needed to avoid compilation error:
//  "The type B1 must implement the inherited abstract method A1.foo()"
//
abstract class A1{                  abstract void foo();    }
interface I1{                       void foo();             }
class B1 extends A1 implements I1{  public void foo(){}     }

// (2a)
// A - abstract method
// I - non-abstract method  
//
// Implementation needed to avoid compilation error:
//  "The type B2 must implement the inherited abstract method A2.foo()"
//
abstract class A2{                  abstract void foo();    }
interface I2{                       default void foo(){}    }
class B2 extends A2 implements I2{  public void foo(){}     }

// (3a) 
// A - non-abstract method  
// I - abstract method
//
// Implementation not needed
//
abstract class A3{                  public void foo(){}     }
interface I3{                       void foo();             }
class B3 extends A3 implements I3{                          }

// (4a)
// A - non-abstract method
// I - non-abstract method  
//
// Implementation not needed
//
abstract class A4              {    public void foo(){System.out.println("A4");}}
interface I4{default void foo(){    System.out.println("I4");}                  }
class B4 extends A4 implements I4{  B4(){foo();} /*prints "A4"*/                }



// (1b) 
// J - abstract method  
// K - abstract method
//
// Implementation needed to avoid compilation error:
//  "The type C1 must implement the inherited abstract method K1.foo()"
//
interface J1{               void foo();         }
interface K1{               void foo();         }
class C1 implements J1,K1{  public void foo(){} }

// (2b)
// J - abstract method
// K - non-abstract method  
//
// Implementation needed to avoid compilation error:
//  "The default method foo() inherited from K2 conflicts with another 
//   method inherited from J2"
//
interface J2{               void foo();             }
interface K2{               default void foo(){}    }
class C2 implements J2,K2{  public void foo(){}     }

// (3b) 
// J - non-abstract method  
// K - abstract method
//
// Implementation needed to avoid compilation error:
//  "The default method foo() inherited from J3 conflicts with another 
//   method inherited from K3"
//
interface J3{               default void foo(){}    }
interface K3{               void foo();             }
class C3 implements J3,K3{  public void foo(){}     }

// (4b)
// J - non-abstract method
// K - non-abstract method  
//
// Implementation needed to avoid compilation error:
//  "Duplicate default methods named foo with the parameters () and () 
//   are inherited from the types K4 and J4"
//
interface J4{               default void foo(){}    }
interface K4{               default void foo(){}    }
class C4 implements J4,K4{  public void foo(){}     }

无论如何,尽管我能够理解本例中的大多数情况,但还是无法推断出任何“一般规则”。

例如,我不理解为什么情况 2a3a的 工作方式不同,即为什么接受了抽象类给出的实现而没有接受接口给出的实现。

我的最后一个问题是:是否真的存在任何“通则”?有什么方法可以预测编译器的行为而不必记住每个案例?

这可能是微不足道的,但我认为这对其他拒绝我考虑的人很有用。

我认为您可以将所有问题归纳为以下简单步骤:

给定这样的示例代码

abstract class A{abstract void foo();}
abstract class B extends A {protected void foo(){}}
interface I{void foo();}
interface J{default void foo(){}}

class C extends B implements I,J{}
  1. 考虑您的类C由其所有方法和继承的方法组成(称为C *)

class C* implements I,J{protected void foo(){};}

  1. 根据它实现的接口验证C *(来自接口的每个方法的歧义,包括默认方法,都必须通过提供实现在C中解决)。

3a。如果C *提供了有效的实现,则在此处停止

(it's not the case because method visibility cannot be reduced from public to protected)

3b。否则,需要在C中执行有效的实现

class C extends B implements I,J{public void foo(){}}


问题答案:

JLS声明(第9.4.1.3节第7段):

当继承具有匹配签名的抽象方法和默认方法时,我们将产生一个错误。

然后,他们继续解释为什么选择此选项:

在这种情况下,可以将一个优先级赋予另一个优先级-也许我们会假设默认方法也为抽象方法提供了合理的实现。但这是有风险的,因为除了巧合的名称和签名之外,
我们没有理由相信默认方法的行为与抽象方法的协定一致 -最初开发子接口时,默认方法甚至可能不存在。
在这种情况下,要求用户主动断言默认实现是适当的(通过覆盖声明),这样比较安全。



 类似资料:
  • 问题内容: 我有一个具有通用方法的抽象类,并且我想通过用特定类型代替通用参数来覆盖通用方法。所以在伪代码中,我有以下内容: 但是由于某种原因,我不允许这样做?我是在犯某种语法错误还是不允许这种继承和覆盖?具体来说,由于eclipse IDE不断提醒我要实现,我遇到了一个错误。 这就是我希望上面的代码起作用的方式。在我的代码的其他地方,有一个方法可以期望实现对象的实例,这具体意味着它们具有我可以使用

  • 这是链接中问题的继续:我正在学习java中的异常处理(基本上是在继承中),子类方法必须抛出异常,该异常是父类方法的子类。 “当子类重写超类中的方法时,子类方法定义只能在父类方法(或重写方法)的throws子句中指定异常类的所有或子集” 这是一条规则。但我不知道制定这条规则的原因是什么,为什么不允许反之亦然。为什么要创建此规则

  • 问题内容: 我之所以学习,是因为我参加了考试,而大多数Java并没有很多问题,但是我偶然发现了一个我无法解释的规则。这是一个代码片段: 返回: 1 3 1 3 虽然我希望它会返回: 1 3 1 4 为什么a2的类型确定在AX中调用哪种方法? 我一直在阅读有关重载规则和继承的文章,但这似乎晦涩难懂,以至于我无法找到确切的规则。任何帮助将不胜感激。 问题答案: 这些方法调用的行为由Java语言规范(参

  • 我一直在读关于在处理子类时如何最好地重写equals方法的文章,在这里我发现了不少帖子。他们建议使用instanceof或getClass()实现解决方案的不同方法来比较不同子类的对象。 然而,关于有效的Java,我的理解是(我对这一点还不熟悉,所以我很可能是错的!)布洛赫认为,最终两者都会有问题,“除非你愿意放弃面向对象抽象的好处,否则没有办法在保留equals契约的同时扩展一个可实例化类并添加

  • 我有一个扩展了B类的a类。 A是这样定义的,它也覆盖了B的方法: B是这样定义的: 因此,如果我初始化A的一个对象,构造函数将调用调用方法doSomething()的超类之一。但哪一个会被处决?B的实现还是A中被重写的实现?

  • 我有一个超时执行任务的方法。我使用ExecutorServer.submit()获取一个Future对象,然后调用future.get()并超时。这很好,但是我的问题是处理我的任务可能抛出的检查异常的最好方法。下面的代码工作正常,并且保留了被检查的异常,但是如果方法签名中被检查的异常的列表改变了,它看起来非常笨拙并且容易出错。 关于如何解决这个问题的任何建议?我需要以Java 5为目标,但我也很好