我刚刚在我们的代码库中找到了一个静态嵌套接口。
class Foo {
public static interface Bar {
/* snip */
}
/* snip */
}
我以前从未见过这种情况。原始开发人员遥不可及。因此我不得不问:
静态接口背后的语义是什么?如果我删除static
? 为什么会有人这样做?
上例中的 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() {...}
});
这个问题已经得到解答,但是使用嵌套接口的一个很好的理由是它的功能是否与其所在的类直接相关。一个很好的例子就是Listener
. 如果您有一个类Foo
,并且希望其他类能够监听其上的事件,则可以声明一个名为 的接口FooListener
,这可以,但声明一个嵌套接口并让其他类实现可能会更清楚Foo.Listener
(嵌套类Foo.Event
也不错)。
成员接口是隐式静态的。可以在不更改代码语义的情况下删除示例中的 static 修饰符。另请参阅 Java 语言规范8.5.1。静态成员类型声明
内部接口必须是静态的才能被访问。该接口不与类的实例相关联,而是与类本身相关联,因此可以使用 访问它Foo.Bar
,如下所示:
public class Baz implements Foo.Bar {
...
}
在大多数情况下,这与静态内部类没有什么不同。
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;
}
}
通常我会看到静态内部类。静态内部类不能引用包含类,而非静态类可以。除非您遇到一些包冲突(在与 Foo 相同的包中已经有一个名为 Bar 的接口),否则我想我会将其设为自己的文件。强制 Foo 和 Bar 之间的逻辑连接也可能是一个设计决策。也许作者打算 Bar 仅与 Foo 一起使用(尽管静态内部接口不会强制执行此操作,只是逻辑连接)
如果您将类 Foo 更改为接口 Foo 上面示例中的“public”关键字也将是多余的,因为
在另一个接口内定义的接口将隐式地公开静态。
1998 年,Philip Wadler 提出了静态接口和非静态接口的区别。
据我所知,使接口成为非静态的唯一区别是它现在可以包含非静态内部类;因此更改不会使任何现有的 Java 程序无效。
例如,他提出了一种表达问题的解决方案,即一方面表达为“你的语言能表达多少”,另一方面表达为“你试图用你的语言表达的术语”之间的不匹配。 .
在他的示例代码中可以看到静态和非静态嵌套接口之间的区别示例:
// This code does NOT compile
class LangF<This extends LangF<This>> {
interface Visitor<R> {
public R forNum(int n);
}
interface Exp {
// since Exp is non-static, it can refer to the type bound to This
public <R> R visit(This.Visitor<R> v);
}
}
他的建议从未在 Java 1.5.0 中提出。因此,所有其他答案都是正确的:静态和非静态嵌套接口没有区别。
在 Java 中,静态接口/类允许接口/类像顶级类一样使用,即可以由其他类声明。所以,你可以这样做:
class Bob
{
void FuncA ()
{
Foo.Bar foobar;
}
}
如果没有静态,以上将无法编译。这样做的好处是您不需要新的源文件来声明接口。它还在视觉上将接口 Bar 与类 Foo 相关联,因为您必须编写 Foo.Bar 并暗示 Foo 类对 Foo.Bar 的实例执行某些操作。
静态意味着包(项目)的任何类部分都可以在不使用指针的情况下访问它。根据具体情况,这可能是有用的,也可能是阻碍的。
“静态”方法有用的完美示例是 Math 类。Math 中的所有方法都是静态的。这意味着您不必特意去创建一个新实例、声明变量并将它们存储在更多变量中,您只需输入数据并获得结果。
静态并不总是那么有用。例如,如果您正在进行案例比较,您可能希望以几种不同的方式存储数据。您不能创建三个具有相同签名的静态方法。你需要3个不同的实例,非静态的,然后你可以比较,因为如果它是静态的,数据不会随着输入而改变。
静态方法适用于一次性返回和快速计算或易于获取的数据。