【问题标题】:Is there such a thing as a "local interface" in Java?Java中是否有“本地接口”之类的东西?
【发布时间】:2014-02-02 21:18:54
【问题描述】:

Java 允许我定义本地抽象类,如下例所示:

public class Foo {

    public void foo() {
        abstract class Bar {          // Bar is a local class in foo() ...
            abstract void bar();
        }

        new Bar() {                   // ... and can be anonymously instantiated
            void bar() {
                System.out.println("Bar!");
            }
        }.bar();
    }
}

由于某种原因,当我尝试定义“本地接口”而不是本地类时,如下所示:

public class Foo {

    public void foo() {
        interface Bar {           // Bar was supposed to be a local interface...
            void bar();
        }

        new Bar() {               // ... to be anonymously instantiated
            void bar() {
                System.out.println("Bar!");
            }
        }.bar();
    }
}

Java 抱怨“成员接口 Bar 只能在顶级类或接口中定义”。是否有一个原因?还是我错过了我犯的错误?

【问题讨论】:

  • 为什么本地抽象类不能完成这项工作?
  • @TedHopp 确实如此。界面将简单地“更清洁”(在我看来)。在我的实际代码中使用本地类的唯一原因是让它实现另外两个接口(参见stackoverflow.com/questions/21515693/…)。
  • 哇,我不知道甚至可以在方法中定义本地类。我只是好奇,有人知道这个构造实际上有用的一些真实示例吗?
  • @JakubJirutka 我为此使用它们:stackoverflow.com/questions/21515693/…
  • 我认为您在限制界面范围方面过于努力。只需让它成为班级的私人成员。这不是糟糕的设计。

标签: java interface local-class


【解决方案1】:

JLS 中根本没有它的定义。它只是不存在。

至于弱的原因,照JLS 14.3

所有本地类都是内部类(第 8.1.3 节)。

接口不能是内部的 (JLS 8.1.3):

成员接口(第 8.5 节)是隐式静态的,因此它们永远不会被视为内部类。

所以我们不能有本地接口。

我猜,除了 @SotiriosDelimanolis 发现 InterfaceDeclaration 不是 BlockStatement 之外。

【讨论】:

  • 有趣的是:如果您查看“内部类”(docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html)的“官方”示例,它会在相应的接口定义(DataStructureIterator)旁边显示内部类定义(EvenIterator) ,我想通过对称性也应该称为“内部”...... :)
  • @MarkusA。 嵌套 接口是隐式静态的。你不能有一个内部接口。
  • 啊!现在这很有意义!接口是隐式静态的!这就是原因!如果我尝试将本地类定义为静态(... static abstract class Bar ... 在我的第一个示例中引发错误),它也会抱怨。而且,当然,在方法中将任何东西定义为“静态”是没有意义的......
  • @MarkusA。小心你试图建立的联系。 A local class is a nested class (§8) that is not a member of any class and that has a name (§6.2, §6.7).。本地类不是定义它的类的成员,那么为什么会有接口呢?
  • @SotiriosDelimanolis 点...我通常认为接口以编程方式(尽管不是逻辑上)主要等同于抽象类(没有字段,只有抽象方法)。因此,界面上的隐含“静态”有效地再次恢复了这种情况下的对应关系...... PS:一定会喜欢示例 14.3-1。 (本地类声明)在您的链接中。真是一团糟! :) 但是再看两遍,几乎所有的作用域都与局部变量声明的工作方式完全一致......深入到 Java 内部才有意义(至少对我来说)......
【解决方案2】:

Java 语言规范并没有告诉您为什么它是这样设计的,但它确实描述了什么是允许的,什么是不允许的。

method body 具有以下形式

MethodBody:
    Block 
    ;

where Block is

Block:
    { BlockStatementsopt }

BlockStatements:
    BlockStatement
    BlockStatements BlockStatement

BlockStatement:
    LocalVariableDeclarationStatement
    ClassDeclaration
    Statement

因此允许类声明,但不允许使用接口。


我们可以争辩说,从调用者的角度来看,拥有一个本地接口并不是很有用。它没有任何目的。接口旨在描述行为,但由于接口是本地的,因此调用者无法使用它。您也可以在一个类中定义和实现该行为。

【讨论】:

  • 我猜这个接口的目的是“为外部调用者定义行为”,这在这种情况下是没有意义的,这可能是他们决定禁止它的原因。
  • 我认为 Radiodef 找到了真正的原因(见下文)。
  • @MarkusA。好吧,就真正的原因而言,我认为这只是“它不存在”。本地接口不在规范中。
  • @Radiodef 这是它抛出异常的真正原因,但不是它不在规范中的真正原因。 ;) 总的来说,我认为我在 Java 中没有遇到过这样的例子,我最终没有找到为什么应该禁止某些事情的原因(这并不是因为缺乏深入挖掘)。在大多数情况下,我发现 JLS 具有令人印象深刻的意义。我总是对他们设法考虑了多少完全晦涩难懂的结构感到惊讶。我现在唯一记得的对我来说仍然没有意义的是三元运算符的自动装箱行为。 :)
【解决方案3】:

本地接口(和枚举)已与记录类功能一起引入:

不幸的是,此功能在文档中有点模糊 - 但它确实有效。

两个版本都允许编写如下代码:

public class Main {

    public int foo() {

        interface Experimentable {
            int bar();
        }

        Experimentable e = new Experimentable() {
            @Override
            public int bar() {
                return 0;
            }
        };

        return e.bar();
    }

    public static void main(String[] args) {
        System.out.println(new Main().foo());
    }

}

【讨论】:

    【解决方案4】:

    Java 现在(从 16 开始)支持local interface

    本地接口是嵌套接口(第 9 节(接口)),其声明立即包含在块中。

    并且您的(略微更正,因为bar 实现必须是公开的)代码编译得很好。

    public class Foo {
    
        public void foo() {
            interface Bar {           // Bar was supposed to be a local interface...
                void bar();
            }
    
            new Bar() {               // ... to be anonymously instantiated
                public void bar() {
                    System.out.println("Bar!");
                }
            }.bar();
        }
    }
    

    编译:

    ~/tmp $ /usr/local/opt/java/bin/javac -version
    javac 17
    ~/tmp $ /usr/local/opt/java/bin/javac Foo.java
    

    【讨论】:

      猜你喜欢
      • 2023-03-08
      • 1970-01-01
      • 1970-01-01
      • 2012-03-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-07
      • 1970-01-01
      相关资源
      最近更新 更多