Classes and objects
Classes and objects are programming constructs that enable us to abstract and group together data and related functionality into convenient design units both at a conceptual level (classes) and in terms of concrete runtime organization (objects instantiated from classes).
Let us consider one simple example to recall how to define classes and work with them.
Suppose we need to work with numerical values in an application so that we need to build derived values from existing values. We can support such an application in programming terms by first declaring an abstract base class (or trait) that enables us to read a numerical value and nothing else.
Let us declare such a base class (copy and paste this to the console to play yourself!):
abstract class Value: def read: Int end Value
An abstract class in itself does not enable us to do anything unless we extend it with concrete classes that supply concrete implementations to the abstract methods.
Let us implement a value that evaluates to a constant that is given at construction time when we instatiate the class to a concrete object.
Observe how we declare the class to extend the abstract class and provide a concrete implementation to the abstract method:
class ConstantValue(c: Int) extends Value: def read = c end ConstantValue
We can now start instantiating
Value-objects at the console
and read their values:
scala> val v = new ConstantValue(123) v: ConstantValue = ConstantValue@4bd3db5c scala> v.read res: Int = 123
Beyond constant values, we can of course create a class whose values are variable and can be set (written) after the object is created. Let us introduce such a class:
class VariableValue(c: Int) extends Value: var myVal = c def write(v: Int) = myVal = v def read = myVal end VariableValue
We observe that we can indeed now write values to a
and read the new value after a write:
scala> val v = new VariableValue(456) v: VariableValue = VariableValue@5aef577f scala> v.read res: Int = 456 scala> v.write(111) scala> v.read res: Int = 111
Finally, let us create a class that reports the sum of two
values. Observe how we use recursion on the two values
v2 to get the sum:
class SumValue(v1: Value, v2: Value) extends Value: def read = v1.read + v2.read end SumValue
At the console we observe that the value of a
dutifully evaluates to the sum of the two component values
as they evolve with writes:
scala> val v1 = new VariableValue(10) v1: VariableValue = VariableValue@2b35b5d9 scala> val v2 = new VariableValue(20) v2: VariableValue = VariableValue@e0a1ba9 scala> val s = new SumValue(v1,v2) s: SumValue = SumValue@52de94b8 scala> s.read res: Int = 30 scala> v1.write(0) scala> s.read res: Int = 20 scala> v2.write(0) scala> s.read res: Int = 0
We could now build arbitrarily complex sums of values consisting of constant and variable values and previous sum values. Accordingly, we could extend functionality further by introducing, say, negated values, difference values, product values, and so forth. This will be pursued in the exercises in the context of polynomials.