为什么在Java中使用静态嵌套接口?

Mo.*_*Mo. 233 java static interface

我刚刚在代码库中找到了一个静态嵌套接口.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}
Run Code Online (Sandbox Code Playgroud)

我以前从未见过这个.最初的开发者是遥不可及的.因此我不得不问:

静态接口背后的语义是什么?如果我删除了会有什么变化static?为什么有人会这样做?

Jes*_*ick 291

上例中的static关键字是冗余的(嵌套接口自动为"静态"),可以删除而不影响语义; 我建议将其删除.接口方法中的"public"和接口字段上的"public final"也是如此 - 修饰符是冗余的,只是为源代码添加了混乱.

无论哪种方式,开发人员只是声明一个名为Foo.Bar的接口.除了无法访问Foo的代码也无法访问Foo.Bar之外,没有与封闭类的进一步关联.(从源代码 - 字节码或反射可以访问Foo.Bar,即使Foo是包私有的!)

如果您希望仅从外部类中使用嵌套接口,则以这种方式创建嵌套接口是可接受的样式,这样您就不会创建新的顶级名称.例如:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
Run Code Online (Sandbox Code Playgroud)

  • “字节码……可以访问 Foo.Bar”是指引用 Foo.Bar 的编译类可以加载和运行,即使它不能引用 Foo。如果该类是在 Foo 是公共的较早时间编译的,或者如果该类是手工组装的或从某种非 Java 语言编译等,则可能会发生这种情况。 但是,Java 编译器会检查封闭类上的访问修饰符即使生成的字节码不会引用该封闭类。 (2认同)

Col*_*inD 71

这个问题已得到解答,但使用嵌套接口的一个很好的理由是它的函数与它所在的类直接相关.一个很好的例子是a Listener.如果你有一个类,Foo并且你希望其他类能够监听它上面的事件,你可以声明一个名为的接口FooListener,这是可以的,但是声明一个嵌套接口并实现其他类可能会更清楚Foo.Listener(嵌套类Foo.Event也不错()).

  • 一个典型的例子是`java.util.Map.Entry`(它是嵌套在另一个接口中的接口). (10认同)
  • 我知道这是一个古老的主题,但我更喜欢外部类在它自己的包中,并且任何补充接口(例如`Map.Entry`)或类也在该包中.我之所以这么说,是因为我喜欢让我的课程简短而重要.读者还可以通过查看包中的类来查看其他实体与类相关的内容.我可能有一个用于map的包`java.collections.map`.这是关于OO和模块化.`java.util`里面太多了.`util`就像`普通' - 一种气味的IMO (4认同)

Bas*_*ers 14

成员接口是隐式静态的.可以删除示例中的static修饰符,而无需更改代码的语义.另请参阅Java语言规范8.5.1.静态成员类型声明


Cli*_*ach 9

内部接口必须是静态的才能被访问.接口不与类的实例相关联,但与类本身相关联,因此可以使用它来访问Foo.Bar,如下所示:

public class Baz implements Foo.Bar {
   ...
}
Run Code Online (Sandbox Code Playgroud)

在大多数情况下,这与静态内部类没有什么不同.

  • 无论是否写入关键字,嵌套接口都是自动静态的. (35认同)
  • 我们真的需要一种方式让社区投票接受不同的答案:http://stackoverflow.com/a/74400/632951 (3认同)
  • ['静态内在'是一个矛盾的术语](http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.1.3). (2认同)

小智 6

Jesse的答案很接近,但我认为有一个更好的代码来证明为什么内部接口可能有用.在阅读之前,请查看下面的代码.你能找到为什么内部接口有用吗?答案是类DoSomethingAlready可以用任何实现A和C的类实例化; 不仅仅是具体的动物园类.当然,即使AC不是内在的,也可以实现这一点,但想象一下连接更长的名称(不仅仅是A和C),并且为其他组合(比如A和B,C和B等)做这个并且你很容易看看事情如何失控.更不用说审查源代码树的人将被仅在一个类中有意义的接口所淹没.总而言之,内部接口允许构建自定义类型并改进其封装.

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是无稽之谈。类 `Zoo` 不*不*实现接口 `AC`,因此,`Zoo` 的实例不能传递给需要 `AC` 的 `DoSomethingAlready` 的构造函数。`AC` 扩展了 `A` 和 `C` 的事实并不意味着实现 `A` 和 `C` 的类也神奇地实现了 `AC`。 (2认同)