scala中的内部类
scala的内部类与java的内部类有些不同,Java中的内部类是包含了它的类的成员,Scala中,内部类的作用域边界就是它的外部类,其实这句话并不是那么容易理解的。本文先介绍一下scala的内部类使用语法,接着翻译一篇文章内部类的使用来深层次分析内部类在两种语言中使用方式的不同。
scala内部类使用语法
下面的代码演示了四种内部类表现形式,以及如何访问内部类。
- 外部类class - 内部类class
- 外部类class - 内部对象object
- 外部对象object - 内部类class
- 外部对象object - 内部对象object
/**
* Created by gaosong on 2017-10-13
*/
object InnerClassDriver extends App{
//调用外部类中的内部类对象
println(new OuterClass().InnerObject.y)
//调用外部类中的内部类
val oc = new OuterClass
var ic = new oc.InnerClass
println(ic.x)
//调用外部对象的内部类
println((new OuterObject.InnerClass).m)
//调用外部对象的内部对象
println(OuterObject.InnerObject.n)
}
class OuterClass{
class InnerClass{
var x = 1
}
object InnerObject{
val y = 2
}
}
object OuterObject{
class InnerClass{
var m = 3
}
object InnerObject{
var n = 4
}
}
scala与java内部类的差别
现在有一个图形类Graph(有一个newNode
方法,返回一个Node
实例),内部有一个结点类Node(有一个connect(other: Node)
方法用于连接另一个结点实例)。其余的细节这里不做说明,用scala语言来表现如下:
class Graph {
class Node {
var connectedNodes: List[Node] = Nil
def connectTo(node: Node) {
if (connectedNodes.find(node.equals).isEmpty) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
实际项目中,可能有一个graph实例,其内部有若干个node,且这若干个node部分连接在了一起,代码表现如下:
object GraphTest extends Application {
val g = new Graph
val n1 = g.newNode
val n2 = g.newNode
val n3 = g.newNode
n1.connectTo(n2)
n3.connectTo(n1)
}
下面将更近一步,返回的实例n1,n2等加上具体的类型,代码如下:
object GraphTest extends Application {
val g: Graph = new Graph
val n1: g.Node = g.newNode
val n2: g.Node = g.newNode
val n3: g.Node = g.newNode
n1.connectTo(n2)
n3.connectTo(n1)
}
可以看到n1等实例的类型是以外部类的实例g开头的,好的,我们先记住这个特点,再来看一个非法的代码示例:
object IllegalGraphTest extends Application {
val g: Graph = new Graph
val n1: g.Node = g.newNode
val n2: g.Node = g.newNode
n1.connectTo(n2) // legal
val h: Graph = new Graph
val n3: h.Node = h.newNode
n1.connectTo(n3) // illegal!
}
请记住上面代码的最后一行在java实现中却是合法的,因为n1和n3全是Graph.Node
类型 但是在scala中它们的类型是以外部类的实例变量名称为前缀的,在这里是g.Node和h.Node,显然是不同的,所以ConnectTo方法是非法的
另外,如果你想两个不同graph的node能够相互连接,可能你需要对上面的内部类做些修改,如下:
class Graph {
class Node {
var connectedNodes: List[Graph#Node] = Nil
def connectTo(node: Graph#Node) {
if (connectedNodes.find(node.equals).isEmpty) {
connectedNodes = node :: connectedNodes
}
}
}
var nodes: List[Node] = Nil
def newNode: Node = {
val res = new Node
nodes = res :: nodes
res
}
}
Node类中connectedNodes
变量和connectTo
方法的参数都是Graph#Node
类型,表明一个Node结点能连接另一个graph的Node结点。于此同时,如果你想放宽限制,Graph类的nodes结点集合可以拥有其他Graph的Node,那么你可能还需要改变nodes
变量的类型和newNode
方法的类型为Graph#Node
。
但是在实际情况下,这种情况可能不太常见。
一个graph内部的结点node为什么能够连接其他graph内部的结点node呢?两个graph之间一般情况下是不会产生连接关系的,就好像两张图片的点像素一样不会有关联关系,另外一个图片内的nodes集合中为什么会有其他graph内部的node呢?这也是不太合适的。