Object Oriented Programming

Basic

class Person(name: String) // constructor

val person = new Person("John")

Parameters vs Fields

Class parameters can't be accessed by . because they aren't class fields. For example, person.name isn't working.

To create a class field, we need to use val or var.

class Person(name: String, val age: Int)

val person = new Person("John", 26)
println(person.age) // works. Because age is a class field.

Body

class Person(name: String, val age: Int) {
  // body

  // fields
  val x: Int = 2

  // methods
  def greet(name: String): Unit = println($"Hi $name.")
  // Overloading
  def greet(): Unit = println($"I'm ${this.name}. I'm ${this.age} years old.")
}

val person = new Person("John", 26)
println(person.x) // works.
  • When an instance is initialized, all the codes in the body are run.
  • val Values in the body are class fields.
  • this points to both class fields and methods.
  • In general, overloading methods works in Scala except one case, which is two methods have the same name and parameters but return different types of data.

Immutability

Immutability is very important in Functional Programming.

When you need to modify the contents of an instance, you create a new instance.

class Counter(val num: Int) {
  def increase(n: Int = 1) = new Counter(this.num + n)
  def decrease(n: Int = 1) = new Counter(this.num - n)
}

Operators

All operators in Scala are methods.

val num = 1 + 2
val num = 1.+(2) // equivalent

Therefore, we can override or create an operator.

class Person(val: name) {
  def worksWith(person: Person): String = s"${this.name} works with ${person.name}."

  def +(person: Person): String = s"${this.name} works with ${person.name}."
}

val jack = new Person("Jack")
val paul = new Person("Paul")

println(jack + paul) // Jack works with Paul.
println(jack worksWith paul) // Jack works with Paul.

There is a very special method in Scala Class, apply.

class Person(val: name) {
  def apply(): String = s"I'm ${this.name}."
  def apply(val greeting: String): String = s"$greeting, I'm ${this.name}."
}

val mary = new Person("Mary")

mary.apply() // I'm Mary.
mary() // I'm Mary.

mary.apply("Hi")  // Hi, I'm Mary
mary("Hi")  // Hi, I'm Mary

apply method breaks the barrier between OOP and Functional Programming. So it's heavily used in Scala.

Notation

class Person(val name: String) {
  // infix notation
  def worksWith(person: Person): String = s"${this.name} works with ${person.name}."
  // prefix notation
  def unary_!(): String = s"${name.toUpperCase()}!"
  // postfix notation
  def isAlive(): Boolean = true;
  // apply
  def apply(): String = s"I'm ${this.name}."
}
  • Infix notion

    jack.worksWith(paul)
    jack worksWith paul // equivalent
    

    It only works on the method with ONE parameter.

  • prefix notion

    jack.unary_!()
    !jack // equivalent
    

    It only works with +, -, ~, !.

  • postfix notation

    jack.isAlive()
    jack isAlive // equivalent
    

    It only works on the method with NO parameters.

  • apply

    The special method let us call the instance of a class as a function.