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

为什么必须用Java声明接口?

阎阳
2023-03-14
问题内容

有时我们有几个类,这些类的某些方法具有相同的签名,但是与声明的Java接口不对应。例如,JTextFieldJButton(在中的其他几个
javax.swing.*)中都有一个方法

public void addActionListener(ActionListener l)

现在,假设我希望对具有该方法的对象进行一些操作。然后,我想有一个接口(或者自己定义),例如

  public interface CanAddActionListener {
      public void addActionListener(ActionListener l);
  }

这样我可以写:

  public void myMethod(CanAddActionListener aaa, ActionListener li) {
         aaa.addActionListener(li);
         ....

但是,可悲的是,我不能:

     JButton button;
     ActionListener li;
     ...
     this.myMethod((CanAddActionListener)button,li);

此演员表将是非法的。编译器 知道JButton 是不是
一个CanAddActionListener,因为类没有宣布实现该接口...... 然而“实际上”实现它

有时这会带来不便-Java本身已经修改了几个核心类,以实现由旧方法(String implements CharSequence例如)组成的新接口。

我的问题是:为什么会这样呢?我了解声明类实现接口的实用程序。但是无论如何,看看我的示例,为什么编译器不能推断出该类JButton“满足”了接口声明(在其内部)并接受了强制转换?是编译器效率问题还是更多基本问题?

我对答案的总结 :在这种情况下,Java可能会允许某些“结构类型”(某种鸭子类型-
但在编译时进行了检查)。没有。除了一些(对我来说还不清楚)性能和实现方面的困难之外,这里还有一个更基本的概念:在Java中,接口(以及所有事物的声明)的声明并不仅仅意味着
结构化 (具有这些签名),但在 语义上 :方法应该实现某些特定的行为/意图。因此,在 结构上
满足某些接口的类(即,它具有带有所需签名的方法)不一定在 语义上 满足它 __(一个极端的例子:回想一下甚至没有方法的“标记接口”!)。
因此,Java可以断言一个类实现了一个接口,因为(并且仅因为)该接口已被明确声明。其他语言(Go,Scala)也有其他哲学。


问题答案:

Java使实现类明确声明其实现的接口的设计选择就是设计选择。可以肯定的是,JVM已经针对此选择进行了优化,并且除非添加了一些新的JVM指令,否则 现在
可能要付出额外的代价来实现另一个选择(例如Scala的结构化类型)。

那么设计选择到底 什么呢?一切都归结为方法的语义。考虑:以下方法在语义上是否相同?

  • draw(String graphicShapeName)
  • draw(String handgunName)
  • draw(String playingCardName)

这三种方法都具有签名draw(String)。人们可能会通过使用参数名或阅读一些文档来推断他们具有不同的语义。机器有什么方法可以告诉他们它们不同吗?

Java的设计选择是要求类的开发人员明确声明一种方法符合预定义接口的语义:

interface GraphicalDisplay {
    ...
    void draw(String graphicalShapeName);
    ...
}

class JavascriptCanvas implements GraphicalDisplay {
    ...
    public void draw(String shape);
    ...
}

毫无疑问,其中的draw方法JavascriptCanvas旨在与draw图形显示的方法匹配。如果有人试图通过将要拔出手枪的物体,则机器可以检测到错误。

Go的设计选择更为自由,并允许在事后定义接口。具体的类无需声明其实现的接口。而是,新纸牌游戏组件的设计者可以声明提供纸牌的对象必须具有与签名匹配的方法draw(String)。这样做的好处是,可以使用具有该方法的任何现有类,而无需更改其源代码,但是缺点是该类可能会拔出手枪而不是纸牌。

鸭式语言的设计选择是完全放弃形式化接口,并简单地匹配方法签名。接口(或“协议”)的任何概念都是纯惯用语,没有直接的语言支持。

这些只是许多可能的设计选择中的三个。这三个可以概括如下:

Java:程序员必须明确声明他的意图,然后机器会对其进行检查。假设程序员可能会犯语义错误(图形/手枪/卡)。

继续:程序员必须声明其意图的至少一部分,但是检查机器时机器可以继续运行。假设程序员可能会犯一个笔误(整数/字符串),而不会犯语义上的错误(图形/手枪/卡)。

鸭式打字:程序员无需表达任何意图,机器无需检查。假定程序员不太可能犯文书或语义错误。

解决接口和一般键入是否足以测试笔误和语义错误超出了此答案的范围。完整的讨论将不得不考虑构建时编译器技术,自动化测试方法,运行时/热点编译以及许多其他问题。

公认的draw(String)是,故意夸大示例以说明问题。实际示例将涉及更丰富的类型,这些类型将提供更多线索来消除方法的歧义。



 类似资料:
  • 问题内容: 在开始阅读之前:这个问题不是关于理解monad的问题,而是关于确定Java类型系统的局限性的,这会限制接口的声明。 在我的努力去理解单子我读这个由埃里克利珀SO- 答案上询问单子简单的解释问题。在那里,他还列出了可以在monad上执行的操作: 有一种方法可以将未放大类型的值转换为放大类型的值。 有一种方法可以将未放大类型的操作转换为遵循前面提到的功能组成规则的放大类型的操作 通常,有一

  • 我正在开发一个简单的 C 程序,并且很难理解我遇到的编译器错误。该问题是由于我尝试从基类创建派生类引起的。我在下面发布了具有相同结构的代码,但更改了名称。 BaseClass.h DerivedClass.h 派生类.cpp 在尝试编译此代码时,所有虚拟方法都会出现以下错误: 一旦我在“衍生类”中声明了这些方法,错误就会消失,因为编译器现在知道这些方法了。 然而,我很困惑。为什么有必要重新声明派生

  • 问题内容: 在我看到的所有支持可选参数的编程语言中,都有一个模仿,即可选参数必须出现在声明的末尾。可选项目后不得包含必需的参数。是什么原因呢?我想这可能是编译器/解释器的要求。 问题答案: 好吧,如果它们在最前面,您将如何检测何时停止供应它们?唯一的方法是 在 可选参数 之后 变量类型是否不同。有点不可思议的要求,因此您只需将它们强制设置为最后是有意义的(省去了用于检测“最终”可选参数的复杂规则的

  • 在Java,如果我们运行: 它将抛出: 但是,在php中,如果我们运行: 它将打印%1。 我怀疑这是否是因为Java是强类型语言,而php是弱类型语言。能不能有人给出一个更深层次、更低层次的解释?

  • 问题内容: 将接口声明为抽象有什么意义?接口方法也是如此。有什么意义吗? 例如。 问题答案: 您在哪里遇到过已发布的代码块,任何旧的Java代码库? 这就是 JLS不得不说: 9.1.1.1抽象接口: 每个接口都是隐式抽象的。该修饰符已过时,不应在新程序中使用。 9.4抽象方法声明: 为了与Java平台的较早版本兼容,出于风格考虑,允许但不鼓励为接口中声明的方法冗余地指定abstract修饰符。

  • 我收到这个错误: ORA-06550:第1行,第25列:PLS-00302:必须声明组件PA\u EXCEPTION\u LIST\u更新:第1行,第7列:PL/SQL:忽略语句。 我不知道我做错了什么。