我刚在代码库中找到了一个静态嵌套接口。
class Foo {
public static interface Bar {
/* snip */
}
/* snip */
}
我以前从未见过这个。最初的开发者是遥不可及的。因此我不得不问:
静态接口背后的语义是什么?如果删除static
,会有什么变化?为什么有人会这样做?
答案 0 :(得分:290)
上例中的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() {...}
});
答案 1 :(得分:71)
问题已得到解答,但使用嵌套接口的一个很好的理由是它的功能与它所在的类直接相关。一个很好的例子是Listener
。如果你有一个类Foo
,并且你希望其他类能够监听它上面的事件,你可以声明一个名为FooListener
的接口,这没关系,但是声明它可能会更清楚一个嵌套的接口,让其他类实现Foo.Listener
(嵌套类Foo.Event
也不错)。
答案 2 :(得分:13)
成员接口是隐式静态的。可以删除示例中的static修饰符,而无需更改代码的语义。另请参阅Java语言规范8.5.1. Static Member Type Declarations
答案 3 :(得分:9)
内部接口必须是静态的才能被访问。该接口不与类的实例相关联,而是与类本身相关联,因此可以使用Foo.Bar
访问它,如下所示:
public class Baz implements Foo.Bar {
...
}
在大多数情况下,这与静态内部类没有什么不同。
答案 4 :(得分: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;
}
}
答案 5 :(得分:3)
答案 6 :(得分:0)
通常我会看到静态内部类。静态内部类不能引用非静态类所在的包含类。除非你遇到一些包冲突(在与Foo相同的包中已经有一个名为Bar的接口),我想我会把它变成自己的文件。它也可能是强制Foo和Bar之间逻辑连接的设计决策。也许作者希望Bar只与Foo一起使用(虽然静态内部接口不会强制执行此操作,只是逻辑连接)
答案 7 :(得分:0)
如果您将Foo类更改为界面Foo" public"上例中的关键字也是多余的,因为
在另一个界面中定义的界面将隐式公开 静态的。强>
答案 8 :(得分:0)
1998年,Philip Wadler提出了静态接口和非静态接口之间的区别。
据我所知,制作一个的唯一区别 接口非静态是它现在可以包含非静态内部 班;因此,更改不会使任何现有Java无效 程序
例如,他提出了Expression Problem的解决方案,这是表达方式之间的不匹配,一方面是“你的语言能表达多少”,另一方面表达为“你试图在你的语言中表达的术语”语言“另一方面。
his sample code中可以看到静态和非静态嵌套接口之间的区别示例:
// 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中得到过。因此,所有其他答案都是正确的:静态和非静态嵌套接口没有区别。
答案 9 :(得分:-1)
在Java中,静态接口/类允许接口/类像顶级类一样使用,也就是说,它可以由其他类声明。所以,你可以这样做:
class Bob
{
void FuncA ()
{
Foo.Bar foobar;
}
}
如果没有静态,上面的编译就会失败。这样做的好处是您不需要新的源文件来声明接口。它还可以直观地将接口Bar与类Foo相关联,因为你必须编写Foo.Bar并暗示Foo类使用Foo.Bar实例做某事。
答案 10 :(得分:-6)
静态意味着包(项目)的任何类部分都可以在不使用指针的情况下访问它。根据具体情况,这可能是有用的或阻碍的。
“静态”方法的有用元素的完美示例是Math类。 Math中的所有方法都是静态的。这意味着您不必完全忘记,创建新实例,声明变量并将其存储在更多变量中,您只需输入数据并获得结果即可。
静态并不总是那么有用。例如,如果您正在进行大小写比较,则可能需要以多种不同方式存储数据。您无法创建具有相同签名的三个静态方法。您需要3个不同的非静态实例,然后您可以进行比较,如果它是静态的,则数据不会随输入而变化。
静态方法适用于一次性返回和快速计算或易于获取的数据。