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

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

Why would a static nested interface be used in Java?

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

1
2
3
4
5
6
class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

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

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


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

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

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

1
2
3
4
5
6
7
8
9
10
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访问它,如下所示:

1
2
3
public class Baz implements Foo.Bar {
   ...
}

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


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;
  }
}

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

Map.Entry的

这也许有用

静态嵌套Inerfaces博客条目


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

So far as I can see, the only difference in making an
interface non-static is that it can now include non-static inner
classes; so the change would not render invalid any existing Java
programs.

例如,他提出了表达式问题的解决方案,这是表达式之间的不匹配,一方面是"你的语言表达多少",另一方面表达为"你试图用你的语言表达的术语"。 。

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

1
2
3
4
5
6
7
8
9
10
11
// 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中得到过。因此,所有其他答案都是正确的:静态和非静态嵌套接口没有区别。


如果您将类Foo更改为接口Foo,上面示例中的"public"关键字也将是多余的,因为

interface defined inside another interface will implicitly public
static.


通常我会看到静态内部类。静态内部类不能引用非静态类所在的包含类。除非你遇到一些包冲突(在与Foo相同的包中已经有一个名为Bar的接口),我想我会把它变成自己的文件。它也可能是强制Foo和Bar之间逻辑连接的设计决策。也许作者希望Bar只与Foo一起使用(虽然静态内部接口不会强制执行此操作,只是逻辑连接)


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

1
2
3
4
5
6
7
class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

没有静态,上面将无法编译。这样做的好处是您不需要新的源文件来声明接口。它还可以直观地将接口Bar与类Foo相关联,因为您必须编写Foo.Bar并暗示Foo类使用Foo.Bar实例执行某些操作。

Java中类类的描述。


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

"静态"方法的有用元素的完美示例是Math类。 Math中的所有方法都是静态的。这意味着您不必走开,制作新实例,声明变量并将其存储在更多变量中,您只需输入数据并获得结果即可。

静态并不总是那么有用。例如,如果您正在进行大小写比较,则可能需要以多种不同方式存储数据。您无法创建具有相同签名的三个静态方法。您需要3个不同的非静态实例,然后您可以进行比较,如果它是静态的,则数据不会随输入而变化。

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


推荐阅读