🇧🇷 Leia em Português

Classes in Scala: method and attribute definition

A black and white image of a laptop and a notebook faded and on the middle of the image there is the Scala logo in red for the post 3

This post is also known as “My saga learning Scala - Part 3“ and is a continuation of Part 1 and Part 2

As we discussed in the Part 1 of this series, Scala is an object oriented language. From Wikipedia:

Object-oriented programming (OOP) is a programming paradigm based on the concept of “objects”, which can contain data, in the form of fields (often known as attributes or properties), and code, in the form of procedures (often known as methods).

So on this post I will expose what I learned of the Scala classes, which follow this concept of object that I just presented.

Creating a class

To create a class you only need to pass the keyword class and the name of the class:

class Point
    
val point = new Point

This is called the class constructor and you can add the attributes that your class can receive, such as:

class Point(x: Int, y: Int) 

val point = new Point(0, 0)

The fun part starts now! You can actually rewrite the constructor to receive different attributes, making your class have default attributes or even other types of attribute

class Person(name: String, age: Int) {
  def this(name: String) = this(name, 0)
  def this() = this("Bob", 0 )
}

val marco = new Person("Marco", 30)
val babyCarl = new Person("Carl")
val bob = new Person

Adding attributes to a class

The attributes I just added to the class Person can’t be accessed directly, like marco.age on the example above. The thing it that you actually have 3 options to define attributes when defining a class. Not passing anything (like the example above), with a val or with a var.

Using nothing, the attributes you create while instantiating a class won’t work.

class Point(x: Int)

val point = new Point(1)
point.x // won't work

When using val, you can access the attribute (you create an accessor) but can’t change the value (it won’t have a mutator)

class Point(val x: Int)

val point = new Point(1)
println(point.x) // 1
point.x = 2 // won't work

So this is the same thing as:

class Point(xc: Int) {
    def x = xc
}

// same thing as

class Point(val x: Int)

When using a var you probably guessed already: you will both have a mutator and an accessor!

class Point(var x: Int)

val point = new Point(1)
println(point.x) // 1
point.x = 2 
println(point.x) // 2

So… you have:

Attribute definition Assessor? (point.x) Mutator? (point.x = 4) Can override?
val yes no yes
var yes yes no
nothing no no yes

Using classes

When defining a class, you actually can use other extra keywords to define how you will like your class to behavior. So far I learned 3 major keyword:

Keywords Allowed usage
class Point Normal class that allows creating multiple instances
final class Point Doesn’t allow class to be extended
sealed class Point Allow class to be extended but only in the same file

Class methods and overrides

Since classes have keywords that allows us to change the way they are used, it is reasonable to suppose that they also have keyword that can change their behavior. I created a small table with the properties of each one and examples to help us understand what changes exactly.

Extended class can use? Extended class can overwritte? Instances of extended class can use the method?
1 def / val Yes Yes Yes
2 private def / private val No Don’t need to No
3 protected def/ protected val Yes Yes No
4 final def / final val Yes No Yes

Case 1: def and val

class Person {
  val normalAttribute = 1
  def normalMethod = s"case $normalAttribute"
}

class Leticia extends Person

val leticia = new Leticia
println(leticia.normalAttribute)    // 1
println(leticia.normalMethod)       // case 1

Case 2: private def and private val

class Person {
  private val privateAttribute = 2
  private def privateMethod = s"case $privateAttribute"
}

class Leticia extends Person

val leticia = new Leticia
println(leticia.privateVal) // error
println(leticia.privateMethod) // error

Case 3: protected def and protected val

 class Person {
     protected val protectedAttribute = 3
     protected def protectedMethod = s"case $protectedAttribute"
 }
 
 class Gabi extends Person
 val gabi = new Gabi
 println(gabi.protectedMethod) // error
 
 class Leticia extends Person {
    override val protectedAttribute = super.protectedAttribute
    override def protectedMethod = super.protectedMethod
 }
 val leticia = new Leticia
 println(leticia.protectedMethod) // case 3
 

Case 4: final def/ final val

class Person {
  final val finalAttribute = 4
  final def finalMethod = s"case $finalAttribute"
}

class Leticia extends Person {
  override val finalVal = super.finalAttribute // error
}

class Marco extends Person
val marco = new Marco
println(marco.finalAttribute)                  //case 4

Since I wanted this post to be more like a cheatsheet than a heavy text post… this is it for now 🙂 See you in Part 4 (maybe?)!


Cheers!
Letícia

Comments