Scala Tutorial

Basics

Control Statements

OOP Concepts

Parameterized - Type

Exceptions

Scala Annotation

Methods

String

Scala Packages

Scala Trait

Collections

Scala Options

Miscellaneous Topics

Overriding Accessors and Mutators in Scala

In Scala, getters and setters (commonly referred to as accessors and mutators) are automatically generated for class fields. However, you can override or customize these generated methods. Let's take a deeper dive into this topic.

1. Overriding Default Accessors (Getters)

When you declare a var or a val in a class, Scala automatically generates a getter for it. If it's a var, a setter is also generated. You can override the default getter by defining a method with the same name as the field.

class Person {
  private var _name = "Default"

  def name: String = {
    println("Getting name")
    _name
  }
}

val person = new Person
println(person.name)  // Getting name
                       // Default

In the example above, accessing name will now print "Getting name" before returning the value.

2. Overriding Default Mutators (Setters)

To override the default setter for a var, you can define a method with the name fieldName_=.

class Person {
  private var _name = "Default"

  def name: String = _name

  def name_=(newName: String): Unit = {
    println(s"Setting name to $newName")
    _name = newName
  }
}

val person = new Person
person.name = "John"   // Setting name to John
println(person.name)   // John

In this example, assigning a new value to name prints a message.

3. Leveraging Property Change Logic

By overriding getters and setters, you can introduce additional logic, such as validation or notification mechanisms.

class Circle {
  private var _radius: Double = 1.0

  def radius: Double = _radius

  def radius_=(r: Double): Unit = {
    if (r >= 0) _radius = r
    else println("Radius cannot be negative!")
  }
}

val circle = new Circle
circle.radius = -5   // Radius cannot be negative!

Here, we ensure that the circle's radius can never be set to a negative value.

4. Using @BeanProperty for Java Interoperability

If you're interfacing with Java code that expects traditional "get" and "set" prefixes for accessor and mutator methods, you can use the @BeanProperty annotation:

import scala.beans.BeanProperty

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

val person = new Person
person.setName("John")
println(person.getName)

This generates both the Scala-style and Java-style getter and setter methods.

In conclusion, Scala offers a neat way to override default accessors and mutators, allowing developers to introduce custom behaviors while keeping the code concise and expressive.

  1. Description: Customizing getters and setters allows for fine-grained control over property access and mutation.

  2. Overriding Default Accessors in Scala:

    • Description: Override the default getter to customize property access.
    • Code Example:
      class MyClass(private var _value: Int) {
        def value: Int = {
          // Custom logic for getter
          _value + 10
        }
      }
      
  3. Modifying Default Mutators in Scala:

    • Description: Modify the default setter to customize property mutation.
    • Code Example:
      class MyClass(private var _value: Int) {
        def value_=(newValue: Int): Unit = {
          // Custom logic for setter
          _value = newValue * 2
        }
      }
      
  4. Advanced Property Access in Scala:

    • Description: Combine custom getter and setter logic for advanced property access.
    • Code Example:
      class MyClass(private var _value: Int) {
        def value: Int = {
          // Custom logic for getter
          _value + 10
        }
      
        def value_=(newValue: Int): Unit = {
          // Custom logic for setter
          _value = newValue * 2
        }
      }
      
  5. Using 'override' Keyword for Accessors in Scala:

    • Description: Use the 'override' keyword when providing custom accessors in a subclass.
    • Code Example:
      class MySubclass extends MyClass(5) {
        override def value: Int = {
          // Custom logic in subclass getter
          super.value * 2
        }
      }
      
  6. Changing Visibility of Accessors and Mutators in Scala:

    • Description: Adjust the visibility of accessors and mutators for encapsulation.
    • Code Example:
      class MyClass(private var _value: Int) {
        private var _secret: String = "hidden"
      
        def value: Int = _value
        private def secret: String = _secret
      }
      
  7. Overriding Accessors and Mutators in Scala Traits:

    • Description: Traits can provide default accessors and mutators, and they can be overridden in classes.
    • Code Example:
      trait Loggable {
        var logMessage: String = "Default Log"
      
        def log(): Unit = println(logMessage)
      }
      
      class MyClass extends Loggable {
        override def logMessage: String = "Custom Log Message"
      }
      
  8. Encapsulation in Scala with Custom Accessors and Mutators:

    • Description: Achieve encapsulation by controlling access and mutation of properties.
    • Code Example:
      class BankAccount(private var _balance: Double) {
        def balance: Double = _balance
      
        def deposit(amount: Double): Unit = {
          // Custom logic for deposit
          _balance += amount
        }
      
        def withdraw(amount: Double): Unit = {
          // Custom logic for withdrawal
          if (_balance >= amount) _balance -= amount
        }
      }