Lesson 4: Scala Programming Language Tutorial

Lesson 4: Scala Programming Language Tutorial

This lesson provides four Scala code examples, each explained in four detailed points of around 25 words each. Each explanation point repeats the relevant code fragment for clarity.


Example 1: Traits in Scala

trait Animal {
  def makeSound(): String
}

class Dog extends Animal {
  def makeSound(): String = "Bark"
}

val myDog = new Dog()
println(myDog.makeSound())

Explanation:

  1. trait Animal { def makeSound(): String } – Defines a trait named Animal, which acts as an interface requiring the method makeSound to be implemented by subclasses.

  2. class Dog extends Animal { def makeSound(): String = "Bark" } – The Dog class extends Animal, implementing makeSound to return "Bark", fulfilling the trait’s contract.

  3. val myDog = new Dog() – Instantiates an object myDog from the Dog class, which automatically inherits the makeSound method from the Animal trait.

  4. println(myDog.makeSound()) – Calls makeSound() on myDog, printing "Bark". This demonstrates how traits enforce method definitions and allow reuse across multiple classes.


Example 2: Companion Objects in Scala

class Person(val name: String)

object Person {
  def create(name: String): Person = new Person(name)
}

val john = Person.create("John")
println(john.name)

Explanation:

  1. class Person(val name: String) – Defines a class Person with a primary constructor that initializes the immutable name property when a new object is created.

  2. object Person { def create(name: String): Person = new Person(name) } – Declares a companion object Person, containing a factory method create that constructs a Person instance.

  3. val john = Person.create("John") – Calls the create method from Person’s companion object, instantiating a Person object with "John" as the name.

  4. println(john.name) – Prints john.name, outputting "John". This pattern is useful for factory methods and ensuring controlled object creation.


Example 3: Case Classes and Pattern Matching

case class User(name: String, age: Int)

val user = User("Alice", 25)

user match {
  case User("Alice", 25) => println("Matched Alice!")
  case _ => println("No match found.")
}

Explanation:

  1. case class User(name: String, age: Int) – Defines a case class named User. Case classes automatically generate equality checks, toString, and copy methods, simplifying data modeling.

  2. val user = User("Alice", 25) – Instantiates a User object without using new. Case classes provide concise object creation syntax, improving readability.

  3. user match { case User("Alice", 25) => println("Matched Alice!") – Uses pattern matching to check if user has name = "Alice" and age = 25. If true, "Matched Alice!" is printed.

  4. case _ => println("No match found.") – The wildcard _ case ensures all unmatched values print "No match found.", demonstrating exhaustive pattern matching in Scala.


Example 4: Futures and Asynchronous Programming

import scala.concurrent.{Future, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global

val futureResult = Future {
  Thread.sleep(1000)
  "Task Completed"
}

futureResult.foreach(println)
Thread.sleep(2000)

Explanation:

  1. import scala.concurrent.{Future, ExecutionContext} – Imports necessary classes to use Future, a feature that allows concurrent computation execution without blocking the main thread.

  2. val futureResult = Future { Thread.sleep(1000); "Task Completed" } – Creates a Future that simulates an asynchronous task by sleeping for 1 second before returning "Task Completed".

  3. futureResult.foreach(println) – Attaches a callback using foreach, printing "Task Completed" when the future finishes execution asynchronously.

  4. Thread.sleep(2000) – Ensures the main thread waits 2 seconds to let the future complete, demonstrating non-blocking asynchronous programming in Scala.