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

我刚刚在我们的代码库中发现了一个静态嵌套接口。

class Foo {
public static interface Bar {
/* snip */
}
/* snip */
}

我以前从未见过这个。原来的开发人员已经找不到了。因此我不得不问SO:

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

81980 次浏览

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

public class Baz implements Foo.Bar {
...
}

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

要非常直接地回答你的问题,请查看Map.Entry。

地图。条目< / >

这也可能是有用的

静态嵌套Inerfaces博客条目

通常我看到的是静态内部类。静态内部类不能引用包含类,而非静态类可以。除非你遇到一些包碰撞(已经有一个接口称为Bar在相同的包Foo),我想我会让它自己的文件。强制Foo和Bar之间的逻辑连接也可能是一种设计决策。也许作者希望Bar只与Foo一起使用(尽管静态内部接口不会强制执行这一点,只是一个逻辑连接)

静态意味着包(项目)的任何类都可以在不使用指针的情况下访问它。根据情况,这可能是有用的,也可能是阻碍的。

“静态”方法的最佳示例是Math类。数学中的所有方法都是静态的。这意味着你不需要走出去,创建一个新的实例,声明变量并将它们存储在更多的变量中,你只需要输入你的数据并得到一个结果。

静态并不总是那么有用。例如,如果您正在进行案例比较,您可能希望以几种不同的方式存储数据。您不能创建三个具有相同签名的静态方法。你需要3个不同的实例,非静态的,然后你可以比较,因为如果它是静态的,数据不会随着输入而改变。

静态方法适用于一次性返回和快速计算或容易获得的数据。

在Java中,静态接口/类允许接口/类像顶级类一样使用,也就是说,它可以由其他类声明。所以,你可以:

class Bob
{
void FuncA ()
{
Foo.Bar foobar;
}
}

如果没有静态,上述代码将无法编译。这样做的好处是,您不需要一个新的源文件来声明接口。它还可视化地将接口Bar与类Foo关联起来,因为你必须编写Foo。Bar并暗示Foo类对Foo.Bar的实例做一些事情。

Java中类类型的描述

上面例子中的static关键字是多余的(嵌套的接口是自动“静态”的),可以删除而不影响语义;我建议将其删除。接口方法上的“public”和接口字段上的“public final”也是如此——修饰符是多余的,只会给源代码增加混乱。

不管怎样,开发人员只是声明了一个名为Foo.Bar的接口。除了不能访问Foo的代码将不能访问Foo之外,与外围类没有进一步的关联。酒吧。(从源代码-字节码或反射可以访问Foo。即使Foo是包私有的!)

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

public class Foo {
public interface Bar {
void callback();
}
public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
public void callback() {...}
});

成员接口是隐式静态的。可以在不改变代码语义的情况下删除示例中的静态修饰符。请参见Java语言规范8.5.1. 静态成员类型声明

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

Jesse的答案很接近,但我认为有更好的代码可以说明为什么内部接口可能很有用。在继续阅读之前,请查看下面的代码。你能找到为什么内部接口是有用的吗?答案是类DoSomethingAlready可以用实现A和C的任何类实例化;而不仅仅是具体的类Zoo。当然,即使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改为接口Foo,上面例子中的“public”关键字也将是多余的,因为

接口将隐式公共 静态的。< /强> < / p >

1998年,Philip Wadler提出了静态接口和非静态接口之间的区别。

在我看来,唯一的区别是 接口非静态是指它现在可以包含非静态的内部 类;所以这个改变不会使任何现有的Java无效 项目。< / p >

例如,他提出了表达式问题的解决方案,即“你的语言能表达多少”的表达式与“你试图用你的语言表示的术语”的表达式之间的不匹配。

静态和非静态嵌套接口之间区别的一个例子可以在他的示例代码中看到:

// 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中。因此,所有其他答案都是正确的:静态和非静态嵌套接口没有区别。