代码之家  ›  专栏  ›  技术社区  ›  Mo.

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

  •  228
  • Mo.  · 技术社区  · 16 年前

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

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

    我以前从没见过这个。原来的显影剂已经够不着了。因此,我不得不这样问:

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

    11 回复  |  直到 8 年前
        1
  •  288
  •   Jesse Glick    16 年前

    上面例子中的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() {...}
    });
    
        2
  •  71
  •   ColinD    8 年前

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

        3
  •  13
  •   Bas Leijdekkers    8 年前

    成员接口是隐式静态的。可以在不更改代码语义的情况下删除示例中的静态修饰符。参见Java语言规范 8.5.1. Static Member Type Declarations

        4
  •  10
  •   Clinton Dreisbach    16 年前

    内部接口必须是静态的才能被访问。接口不与类的实例关联,而是与类本身关联,因此可以使用 Foo.Bar ,像这样:

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

    在大多数情况下,这与静态内部类没有区别。

        5
  •  6
  •   user1982892    12 年前

    Jesse的答案很接近,但我认为有更好的代码来演示为什么内部接口可能有用。在继续阅读之前,请看下面的代码。你能找到为什么内部接口有用吗?答案是类doSomethingAlready可以用 任何 实现a和c的类;而不仅仅是具体类zoo。当然,即使a c不是内部的,也可以实现这一点,但是可以想象连接更长的名称(不仅仅是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;
      }
    }
    
        6
  •  4
  •   Dhrumil Shah    10 年前

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

    Map.Entry

    这也可能有用

    Static Nested Inerfaces blog Entry

        7
  •  1
  •   Danylo Volokh    10 年前

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

    在另一个接口内定义的接口将 隐式公开 静态的。

        8
  •  0
  •   basszero    16 年前

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

        9
  •  0
  •   Pindatjuh    9 年前

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

    据我所知,唯一不同的是 非静态接口现在可以包含非静态内部 类,因此更改不会使任何现有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);
        }
    }
    

    他的建议从未在Java1.5.0中实现。因此,所有其他答案都是正确的:静态和非静态嵌套接口没有区别。

        10
  •  -1
  •   Tim Cooper    12 年前

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

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

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

    A description of class types in Java .

        11
  •  -6
  •   Vordreller    16 年前

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

    “静态”方法有用性的完美例子是数学类。数学中的所有方法都是静态的。这意味着您不必偏离自己的方向,创建一个新的实例,声明变量并将它们存储在更多的变量中,您只需输入数据并得到一个结果。

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

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