Scala Classes & Objects
Scala Classes and Objects
Section titled “Scala Classes and Objects”Scala is a pure object-oriented language, meaning every value is an object. Classes serve as blueprints for creating these objects, defining their state (fields) and behavior (methods).
Defining Classes:
Section titled “Defining Classes:”A basic class definition includes the class keyword, a name, constructor parameters, and a body containing fields and methods.
// Simple Point class with mutable stateclass Point(initialX: Int, initialY: Int) { // Fields (variables associated with the object) var x: Int = initialX var y: Int = initialY
// Method (behavior) def move(dx: Int, dy: Int): Unit = { x += dx // Same as x = x + dx y += dy // Same as y = y + dy }
// Method to represent the point as a string override def toString: String = s"Point($x, $y)"}Key points:
class Point(...): Defines a class namedPoint.(initialX: Int, initialY: Int): The primary constructor. Its parameters are visible throughout the class body.var x: Int = initialX: Defines a mutable fieldxinitialized with the constructor parameterinitialX.def move(...): Defines a method.override def toString...: Overrides the defaulttoStringmethod inherited fromAnyRef.
Creating Instances (Objects):
Section titled “Creating Instances (Objects):”Use the new keyword followed by the class name and constructor arguments to create an instance (object) of the class.
@main def ClassDemo(): Unit = { // Create an instance of Point val p1 = new Point(10, 20) println(s"Initial point: $p1") // Uses the overridden toString method
// Call methods on the object p1.move(5, -2) println(s"Moved point: $p1")
// Access fields directly (since they are public by default) p1.x = 100 println(s"Manually changed point: $p1")}Output:
Initial point: Point(10, 20)Moved point: Point(15, 18)Manually changed point: Point(100, 18)Constructor Parameters as Fields:
Section titled “Constructor Parameters as Fields:”Scala provides a concise way to declare constructor parameters also as fields by prefixing them with val (immutable field) or var (mutable field).
// More concise Point class definition// xc and yc are now public immutable fieldsclass ConcisePoint(val xc: Int, val yc: Int) { // We can still add more fields and methods private var movedCount = 0
def move(dx: Int, dy: Int): Unit = { // Note: Cannot reassign xc or yc because they are 'val' // To make them mutable, define as: class ConcisePoint(var xc: Int, var yc: Int) println(s"Move called, but cannot change immutable Point($xc, $yc)") movedCount += 1 }
def timesMoved: Int = movedCount
override def toString: String = s"ConcisePoint($xc, $yc)"}Inheritance (Extending Classes):
Section titled “Inheritance (Extending Classes):”Scala supports single inheritance using the extends keyword. A subclass inherits non-private members from its superclass.
Key restrictions:
- Method overriding requires the
overridekeyword. - Only the primary constructor of the subclass can pass parameters to the superclass constructor.
class Point3D(override val xc: Int, override val yc: Int, val zc: Int) extends ConcisePoint(xc, yc) { // Pass params to superclass constructor
var z: Int = zc // Additional field
// Override the move method (if it were possible/desired) // override def move(dx: Int, dy: Int): Unit = { ... }
// Add a new method specific to Point3D def move(dx: Int, dy: Int, dz: Int): Unit = { // Since xc, yc are vals from ConcisePoint, we can't change them here either. // Let's assume ConcisePoint used vars instead for this example to make sense: // super.move(dx, dy) // Call superclass method if ConcisePoint had mutable x,y z += dz println(s"Moved 3D point to roughly ($xc, $yc, $z)") // Accessing inherited xc, yc }
// Override toString again override def toString: String = s"Point3D($xc, $yc, $z)"}
@main def InheritanceDemo(): Unit = { val p3d = new Point3D(1, 2, 3) println(p3d) p3d.move(10, 20, 30) // Calls Point3D's move method println(p3d) println(s"Times base class move logic called: ${p3d.timesMoved}") // Access inherited method}Output (assuming base class move updates state):
Point3D(1, 2, 3)Moved 3D point to roughly (1, 2, 33)Point3D(1, 2, 33)Times base class move logic called: 0 // Because ConcisePoint.move didn't modify stateSingleton Objects:
Section titled “Singleton Objects:”Scala does not have static members like Java. Instead, it has singleton objects, defined using the object keyword. An object definition creates a single instance of that type.
Uses:
- Holding utility methods or constants.
- Implementing the Singleton design pattern.
- Creating entry points for applications (
@mainordef main). - Companion objects (see below).
object Logger { private var level = "INFO"
def setLevel(newLevel: String): Unit = { level = newLevel }
def log(message: String): Unit = { println(s"[$level] $message") }}
@main def ObjectDemo(): Unit = { Logger.log("Application starting...") Logger.setLevel("DEBUG") Logger.log("Detailed diagnostic message.")}Output:
[INFO] Application starting...[DEBUG] Detailed diagnostic message.Companion Objects:
Section titled “Companion Objects:”When a class and a singleton object share the same name and are defined in the same source file, they are called companions. They can access each other’s private members.
Uses:
- Factory methods for the class (often in the
applymethod). - Holding methods or values related to the class but not specific to an instance (similar to static members in Java).
class Circle(radius: Double) { // Class // Can access private members of object Circle def area: Double = Circle.PI * radius * radius private def secretMethod = "Instance secret" def revealCompanionSecret = println(Circle.companionSecret)}
object Circle { // Companion Object private val PI = 3.14159 private def companionSecret = "Companion secret"
// Factory method using apply def apply(radius: Double): Circle = new Circle(radius)
// Can access private members of class Circle def accessInstanceSecret(c: Circle) = println(c.secretMethod)}
@main def CompanionDemo(): Unit = { // Use companion object's apply method as factory val c1 = Circle(5.0) println(s"Circle area: ${c1.area}") c1.revealCompanionSecret // Prints "Companion secret" Circle.accessInstanceSecret(c1) // Prints "Instance secret"}