In this section, we will explore two important concepts in Scala: Abstract Classes and Case Classes. These concepts are fundamental to understanding how to structure and organize your code in a more modular and reusable way.

Abstract Classes

What is an Abstract Class?

An abstract class in Scala is a class that cannot be instantiated directly. It is meant to be extended by other classes. Abstract classes can contain both abstract methods (methods without a body) and concrete methods (methods with a body).

Key Points:

  • Abstract classes are declared using the abstract keyword.
  • They can have both abstract and concrete members.
  • They cannot be instantiated directly.

Syntax:

abstract class Animal {
  def makeSound(): Unit // Abstract method
  def sleep(): Unit = { // Concrete method
    println("Sleeping...")
  }
}

Example:

abstract class Animal {
  def makeSound(): Unit
  def sleep(): Unit = {
    println("Sleeping...")
  }
}

class Dog extends Animal {
  def makeSound(): Unit = {
    println("Woof!")
  }
}

class Cat extends Animal {
  def makeSound(): Unit = {
    println("Meow!")
  }
}

val dog = new Dog()
dog.makeSound() // Output: Woof!
dog.sleep()     // Output: Sleeping...

val cat = new Cat()
cat.makeSound() // Output: Meow!
cat.sleep()     // Output: Sleeping...

Exercise:

  1. Create an abstract class Vehicle with an abstract method move().
  2. Create two classes Car and Bicycle that extend Vehicle and implement the move() method.
  3. Instantiate Car and Bicycle and call their move() methods.

Solution:

abstract class Vehicle {
  def move(): Unit
}

class Car extends Vehicle {
  def move(): Unit = {
    println("Car is moving")
  }
}

class Bicycle extends Vehicle {
  def move(): Unit = {
    println("Bicycle is moving")
  }
}

val car = new Car()
car.move() // Output: Car is moving

val bicycle = new Bicycle()
bicycle.move() // Output: Bicycle is moving

Case Classes

What is a Case Class?

A case class in Scala is a special type of class that is optimized for use in pattern matching. Case classes automatically provide implementations for methods like equals, hashCode, and toString. They also provide a convenient way to create immutable data structures.

Key Points:

  • Case classes are declared using the case class keyword.
  • They automatically provide apply and unapply methods.
  • They are immutable by default.
  • They provide a copy method for creating modified copies of instances.

Syntax:

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

Example:

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

val person1 = Person("Alice", 25)
val person2 = Person("Bob", 30)

println(person1.name) // Output: Alice
println(person2.age)  // Output: 30

// Pattern matching with case classes
person1 match {
  case Person(name, age) => println(s"Name: $name, Age: $age")
}

// Copying with modifications
val person3 = person1.copy(age = 26)
println(person3) // Output: Person(Alice,26)

Exercise:

  1. Create a case class Book with fields title and author.
  2. Instantiate a Book object and print its details.
  3. Use pattern matching to extract and print the title and author of the Book.

Solution:

case class Book(title: String, author: String)

val book = Book("1984", "George Orwell")
println(book) // Output: Book(1984,George Orwell)

book match {
  case Book(title, author) => println(s"Title: $title, Author: $author")
}

Conclusion

In this section, we have covered the basics of abstract classes and case classes in Scala. Abstract classes allow you to define a blueprint for other classes, while case classes provide a convenient way to create immutable data structures and work seamlessly with pattern matching. Understanding these concepts will help you write more modular, reusable, and maintainable code.

Next, we will delve into companion objects and how they can be used to provide additional functionality to your classes.

© Copyright 2024. All rights reserved