I am from OOP
background and and want to clear my idea about Object
, Classes
, Trait
, Sealed Trait
and Case Classes
in Scala
, I am writing below what I understand about it so far:
We create Object
, when we want to code some utility functions inside it and we can access it directly, without using "new" keyword like "Static" classes in Java.
We create Classes
, when we are coding for verb means an object and it's behavior encapsulated same as we code for classes in Java and we instantiate it using "new" keyword.
We create Trait
when we want to code same as Abstract classes in Java
.
We create Sealed Trait
when we want to achieve functionality as same Enum in Java.
We create Case classes
when we can expect that this class can be used for pattern matching in future similar to instanceOf in Java.
Is my understanding correct about all these?
A sealed trait in scala forces all the case classes/objects that wish to extend it to be in the same source file. In other words, if case class B wishes to extend sealed trait A B has to be defined in the same file where trait A is defined.
In Scala, one trait can inherit another trait by using a extends keyword. trait Trait_Name1 extends Trait_Name2 { // Code.. } Traits support multiple inheritance. In Scala, a class can inherit both normal classes or abstract class and traits by using extends keyword before the class name and with keyword before the trait’s name.
Unlike class inheritance, in which each class must inherit from just one superclass, a class can mix in any number of traits. Traits are used to define object types by specifying the signature of the supported methods. Scala also allows traits to be partially implemented but traits may not have constructor parameters.
Case classes can also extend a sealed trait. Thus, objects and case classes can be mixed to create complex hierarchies: sealed trait CelestialBody object CelestialBody { case object Earth extends CelestialBody case object Sun extends CelestialBody case object Moon extends CelestialBody case class Asteroid (name: String) extends CelestialBody }
You're more or less right about most of the facts you've stated if you look into them with an OOP eye. But we have more to them.
Objects in Scala can be seen from a Functional Programming perspective as modules. They do serve the purpose of aggregating similar kinded functions that you have called "utility functions". But they have additional meanings as well.
An object
can be seen as a singleton object, as you can have an object
that inherits a particular trait
or class
.
trait Bird
object Duck extends Bird
You also have the concept of companion object. That is an object that have module functions related to that class and you can even refer private
members of the object from the class.
class Dog {
def eat(food: Food) = Dog.preferredFoods.contains(food)
}
object Dog {
private val preferredFoods = List(Ribeye, DogFood, Banana)
def walk(dog: Dog) = ???
}
You are right about classes. They relate quite close to the Java concept.
One way to view a trait
in Scala is as an abstract class
. But note you can also have an abstract class
in Scala, with the same behaviour as in Java. So what's the difference?
As pointed in the comment, several trait
s can be mixed in together.
Also trait
s can be regarded as a Java interface
if it's completely abstract, that is all methods are abstract as with Java. In fact, if you're targeting interop with Java, that's the way to declare an interface
.
A sealed trait
is just a way to tell the compiler that you won't have any classes or other traits that inherit this one except for the ones in the same file. This serves the purpose of pattern matching as you pointed out about case classes, so the compiler is able to tell if the pattern match is exhaustive with a warning. But also note the Scala has enum
.
Case classes can be used with a sealed trait
for the purpose of pattern matching. But a case class
is more like a "value class". The case
makes the compiler generate a bunch of boilerplate code so you don't have to.
You have an automatic "companion object" so you can instantiate object without the new
, with the auto generated apply
function.
You have automatic hashCode
, equals
, toString
and copy
implementations. And you have automatic val
s for all constructor's parameters.
scala> case class Room(area: Int)
defined class Room
scala> var r = Room(16)
r: Room = Room(16)
scala> r.hashCode
res2: Int = 1313771839
scala> r == Room(16)
res3: Boolean = true
scala> r == Room(15)
res4: Boolean = false
scala> r.toString
res5: String = Room(16)
scala> r.area
res6: Int = 16
scala> case class Point(x: Int, y: Int)
defined class Point
scala> val p = Point(1, 1)
p: Point = Point(1,1)
scala> val p1 = p.copy(y = 0)
p1: Point = Point(1,0)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With