前几天,在reddit上进行了有趣的讨论,即静态内部类。 什么时候太多?
首先,让我们回顾一下Java的基本历史知识。 Java语言提供了四个级别的嵌套类 ,通过“ Java语言”,我的意思是这些构造仅是“语法糖”。 它们在仅了解普通类的JVM中不存在。
(静态)嵌套类
class Outer {static class Inner {}
}
在这种情况下, Inner
完全独立于Outer
,除了公共的共享名称空间。
内部班
class Outer {class Inner {}
}
在这种情况下, Inner
实例对其所在的Outer
实例具有隐式引用。 换句话说,没有关联的Outer
实例就不会有Inner
实例。
创建此类实例的Java方法如下:
Outer.Inner yikes = new Outer().new Inner();
看起来很尴尬的东西很有道理。 考虑在Outer
内部的某个地方创建Inner
实例:
class Outer {class Inner {}void somewhereInside() {// We're already in the scope of Outer.// We don't have to qualify Inner explicitly.Inner aaahOK;// This is what we're used to writing.aaahOK = new Inner();// As all other locally scoped methods, we can// access the Inner constructor by // dereferencing it from "this". We just// hardly ever write "this"aaahOK = this.new Inner();}
}
请注意,就像public
或abstract
关键字一样, static
关键字对于嵌套接口是隐式的。 乍看之下,以下假设语法可能很熟悉……:
class Outer {<non-static> interface Inner {default void doSomething() {Outer.this.doSomething();}}void doSomething() {}
}
……不可能写上面的东西。 除了缺少<non-static>
关键字外,似乎没有任何明显的理由说明“内部接口”不可行。 我怀疑这是通常的情况–必须有一些与向后兼容和/或多重继承有关的边缘警告。
本地班
class Outer {void somewhereInside() {class Inner {}}
}
本地类可能是Java中鲜为人知的功能之一,因为它们几乎没有用处。 本地类是命名类型,其范围仅扩展到封闭方法。 显而易见的用例是当您想在该方法中多次重用这种类型时,例如在JavaFX应用程序中构造多个类似的侦听器。
匿名班
class Outer {Serializable dummy = new Serializable() {};
}
匿名类是只有一个实例的另一种类型的子类型。
嵌套类的前5个用例
如果未在静态上下文中定义它们,则所有匿名类,本地类和内部类都保留对其封闭实例的引用。 如果让这些类的实例泄漏到它们的范围之外,可能会造成很多麻烦。 在我们的文章中阅读有关该麻烦的更多信息: 不要聪明:Double Curly Braces Anti Pattern 。
但是,通常您确实希望从该封闭实例中获利。 具有某种可以在不公开实际实现的情况下返回的“消息”对象可能非常有用:
class Outer {// This implementation is private ...private class Inner implements Message {@Overridepublic void getMessage() {Outer.this.someoneCalledMe();}}// ... but we can return it, being of// type MessageMessage hello() {return new Inner();}void someoneCalledMe() {}
}
但是,对于(静态)嵌套类,没有封闭范围,因为Inner
实例完全独立于任何Outer
实例。 那么,使用这样的嵌套类而不是顶级类型有什么意义呢?
1.与外部类型的关联
如果您想与全世界进行交流,嘿,这种(内部)类型与这种(外部)类型完全相关,并且单独使用没有意义,那么您可以嵌套这些类型。 例如,这是通过Map
和Map.Entry
完成的:
public interface Map<K, V> {interface Entry<K, V> {}
}
2.从外部隐藏外部类型
如果软件包(默认)的可见性不足以满足您的类型的需求,则可以创建private static
类,这些private static
类仅对其封装类型和该封装类型的所有其他嵌套类型可用。 这实际上是静态嵌套类的主要用例。
class Outer {private static class Inner {}
}class Outer2 {Outer.Inner nope;
}
3.保护类型
这确实是一个非常罕见的用例,但是有时,在类层次结构中,您需要只希望对给定类型的子类型可用的类型。 这是protected static
类的用例:
class Parent {protected static class OnlySubtypesCanSeeMe {}protected OnlySubtypesCanSeeMe someMethod() {return new OnlySubtypesCanSeeMe();}
}class Child extends Parent {OnlySubtypesCanSeeMe wow = someMethod();
}
4.模拟模块
与Ceylon不同,Java没有一流的模块 。 使用Maven或OSGi,可以向Java的构建(Maven)或运行时(OSGi)环境中添加一些模块化行为,但是如果要用代码表示模块,则实际上是不可能的。
但是,您可以使用静态嵌套类按照约定建立模块。 让我们看一下java.util.stream
包。 我们可以将其视为一个模块,并且在此模块中,我们有几个“子模块”或类型组,例如内部java.util.stream.Nodes
类,其大致如下所示:
final class Nodes {private Nodes() {}private static abstract class AbstractConcNode {}static final class ConcNode {static final class OfInt {}static final class OfLong {}}private static final class FixedNodeBuilder {}// ...
}
所有java.util.stream
包都可以使用其中一些Nodes
东西,因此我们可以说,它的编写方式如下:
- 合成的
java.util.stream.nodes
子包,仅对java.util.stream
“模块”可见 - 几个
java.util.stream.nodes.*
类型,也仅对java.util.stream
“模块”可见 - 合成
java.util.stream.nodes
包中的几个“顶级”函数(静态方法)
对我来说看起来很像锡兰!
5.外观原因
最后一点很无聊。 或者有些人可能会觉得有趣 。 这是关于品味或易于写作的事情。 有些类是如此之小且无关紧要,将它们写在另一个类中更容易。 为您保存一个.java
文件。 为什么不。
结论
在Java 8时代,考虑Java的非常古老的功能,该语言可能不会被证明是非常令人兴奋的。 静态嵌套类是一些小众用例的很好理解的工具。
但是,本文的重点是这个。 每次嵌套类时,如果您绝对不需要引用封闭的实例,请确保将其设为static
。 您永远不知道何时该引用会在生产中使您的应用程序崩溃 。
翻译自: https://www.javacodegeeks.com/2015/02/top-5-use-cases-for-nested-types.html