5.8 Nested Classes
In Scala, you can nest just about anything inside anything. You can define functions inside other functions, and classes inside other classes. Here is a simple example of the latter:
import scala.collection.mutable.ArrayBuffer
class Network { class Member(val name: String) { val contacts = new ArrayBuffer[Member] } private val members = new ArrayBuffer[Member]
def join(name: String) = { val m = new Member(name) members += m
m } }
Consider two networks:
val chatter = new Network
val myFace = new Network
In Scala, each instance has its own class Member, just like each instance has its own field members. That is, chatter.Member and myFace.Member are different classes.
In our network example, you can add a member within its own network, but not across networks.
val fred = chatter.join("Fred")
val wilma = chatter.join("Wilma")
fred.contacts += wilma // OK
val barney = myFace.join("Barney") // Has type myFace.Member
fred.contacts += barney // No—can't add a myFace.Member to a buffer of chatter.Member elements
For networks of people, this behavior probably makes sense. If you don’t want it, there are two solutions.
First, you can move the Member type somewhere else. A good place would be the Network companion object. (Companion objects are described in Chapter 6.)
object Network { class Member(val name: String) { val contacts = new ArrayBuffer[Member] }
}
class Network { private val members = new ArrayBuffer[Network.Member] ... }
Alternatively, you can use a type projection Network#Member, which means “a Member of any Network.” For example,
class Network { class Member(val name: String) { val contacts = new ArrayBuffer[Network#Member] } ... }
You would do that if you want the fine-grained “inner class per object” feature in some places of your program, but not everywhere. See Chapter 19 for more information about type projections.