Apps 4 U
Apps 4 U

Reputation: 339

Scala best practice on using self in method

HI I have a case when calling a method to use self. Now not sure the best practice to do this in Scala. I have created a example of how I'm doing it just wanted to ask is this the best way of doing so.

sealed trait Animal {
  // match on self
  final def speak(): Unit = {
    this match {
      case Cat(name) => println("spoken like a Cat named: " + name)
      case Dog(name) => println("spoken like a Dog named: " + name)
      case Pig(name) => println("spoken like a Pig named: " + name)
    }
  }
  final def whoAmI(): Unit = {
    this match {
      case Cat(_) => println("I am a Cat")
      case Dog(_) => println("I am a Dog")
      case Pig(_) => println("Could be a Pig")
    }
  }
}
final case class Cat(name: String) extends Animal
final case class Dog(name: String) extends Animal
final case class Pig(name: String) extends Animal

Upvotes: 1

Views: 72

Answers (2)

themathmagician
themathmagician

Reputation: 515

Yes, your use of pattern matching on this is correct. Since your hierarchy is sealed, you know, that there will not be any new types to account for - otherwise you should have the _ => ... clause.

You can express your second case a bit simpler, since you dont care about the parameters, only about the type.

final def whoAmI(): Unit = {
this match {
  case _:Cat => println("I am a Cat")
  case _:Dog => println("I am a Dog")
  case _:Pig => println("Could be a Pig")
}

}

Finally, this always refers to the innermost type. In case of nested inner classes, you might want to use an alias, like self:

trait Tweeter {
   this: self =>  // reassign this into self, so it can be accessed from an inner class

Upvotes: 1

Antot
Antot

Reputation: 3964

If your requirement is only to know which sub-type is used, it can be accessed in a more straightforward manner with just this.getClass():

sealed trait Animal {

  val name: String

  final def speak(): Unit =
    println(s"spoken like a ${this.getClass.getSimpleName} named: ${name}")

  final def whoAmI(): Unit =
    println(s"I am a ${this.getClass.getSimpleName}")

}

If all the implementing sub-types are characterized by a name, that would be convenient to declare an abstract val name: String, which will allow to access the field in speak().

For a use case like this the matchers are not the best option: for each implementing sub-type you have to add an entry in the matchers (may become difficult to maintain). I'd suggest using inheritance: if you have a behavior differentiated among the sub-types, declare an abstract method in the trait, call it from it, but its implementations should remain at sub-types level.

Upvotes: 4

Related Questions