Friday, 17 January 2014

Classes in Scala - Beyond the (Java) Expected

There is a lot more power to the Scala type system than any other language I know, and I’ve yet to come across - let along understand – most of it. (I’ve tried a few times now to compose a post on this topic, but realised each time I was still way off being in a position to do so.) However, in my latest reading of Scala for the Impatient, I’ve come across some interesting extensions to what I learned from Atomic Scala.

Object-Private Fields

As you’d expect (if you’re coming from Java as I have) methods in a (Scala) class can access private fields of all objects which are derived from that class. But you can also add a more restrictive access flag so that objects can only access private fields of the current object:

private[this] var name // access someObject.name is not allowed

example from Scala for the Impatient by Cay Horstmann, pp51

Behind the scenes (which I’m beginning to realise is a good way to grok these things) class-private fields have private getters and setters generated for them, whereas object-private fields have neither getters or setters.

You can, if you like, even go one step further, replacing “private[this]” with “private[className]". In this case, the field can be accessed only by methods of the named class.  Here, the generated code has to generate public getters and setters because the JVM doesn’t support this fine grained level of access control.

Bean Properties

A simple one this. Scala by default creates getters which are simply the same name as the field, and setters which are the name of the field plus “_”.  This doesn’t meet the JavaBeans spec, so to also get the expected getFieldName and setFieldName methods you can:

class Person {
    @BeanProperty var name : String = _
}

example from Scala for the Impatient by Cay Horstmann, pp52

Primary Constructor Parameters

These can use @BeanProperty, private[this] and private[className] forms on top of the “expected” ones.

Additionally, if you declare a Primary Class Constructor Parameter without a val or a var form, then how the parameter is processed is dependent upon how you use the field inside the class. In the following, immutable fields name and age are declared and initialised that are object-private:

class Person(name: String, age: Int) {
    def description = name + " is " + age + " years old"
}

example from Scala for the Impatient by Cay Horstmann, pp55

However, if no method uses the parameters they are not saved as fields at all, and they are instead simply treated as parameters that can be accessed within the Primary Constructor and no more.

As suggested by Martin Odersky and helpfully repeated by Cay, I found it useful to think of Scala classes as being able to take parameters just like methods do.  That makes perfect sense to me.

Before we move on, a final word on making a Primary Constructor private:

class Person private(val id: Int) { ... }

example from Scala for the Impatient by Cay Horstmann, pp57

Lovely for enforcing builders etc, and also just like in Java.  Bosh!

Nested Classes

In our final topic, again, Scala seems hyper-flexible.  Anything (pretty much) can go inside anything else (functions within functions for examples) and it’s no surprise to find out that classes can go within other classes.  I won’t lift the whole example from Cay’s book here (it’s in the free section, go and look at the bottom of page 57) but the important thing to note is that instances have their own classes, just like instances have their own fields too.  That is to say, two nested classes of two different instances of the containing class, are two different classes.

It sounds a little counter intuitive at first, but once you have a play with it it makes more and more sense.  However, if you do want to have things as you’d expect from a Java perspective, you can always stick the nested class definition in the companion object (which, you’ll recall is shared across all instances of a class).  Or (and this pushed me out of my comfort-zone significantly, you can use a type projection, but I’ll not go into that here. I get the impression that once I get to things like that then I’ll really be moving towards the domain of advanced Scala consumer.