Classes and Objects All Versions
This draft deletes the entire topic.
Examples
-
Singleton Objects
Scala supports static members, but not in the same manner as Java. Scala provides an alternative to this called Singleton Objects. Singleton objects are similar to a normal class, except they can not be instantiated using the
new
keyword. Below is a sample singleton class:object Factorial { private val cache = Map[Int, Int]() def getCache = cache }
Note that we have used
object
keyword to define singleton object (instead of 'class' or 'trait'). Since singleton objects can not be instantiated they can not have parameters. Accessing a singleton object looks like this:Factorial.getCache() //returns the cache
Note that this looks exactly like accessing a static method in a Java class.
Companion Objects
In Scala singleton objects may share the name of a corresponding class. In such a scenario the singleton object is referred to as a Companion Object. For instance, below the class
Factorial
is defined, and a companion object (also namedFactorial
) is defined below it. By convention companion objects are defined in the same file as their companion class.class Factorial(num : Int) { def fact(num : Int) : Int = if (num <= 1) 1 else (num * fact(num - 1)) def calculate() : Int = { if (!Factorial.cache.contains(num)) { // num does not exists in cache val output = fact(num) // calculate factorial Factorial.cache += (num -> output) // add new value in cache } Factorial.cache(num) } } object Factorial { private val cache = scala.collection.mutable.Map[Int, Int]() } val factfive = new Factorial(5) factfive.calculate // Calculates the factorial of 5 and stores it factfive.calculate // uses cache this time val factfiveagain = new Factorial(5) factfiveagain.calculate // Also uses cache
In this example we are using a private
cache
to store factorial of a number to save calculation time for repeated numbers.Here
object Factorial
is a companion object andclass Factorial
is its corresponding companion class. Companion objects and classes can access each other'sprivate
members. In the example aboveFactorial
class is accessing the privatecache
member of it's companion object.Note that a new instantiation of the class will still utilize the same companion object, so any modification to member variables of that object will carry over.
-
Primary Constructor
In Scala the primary constructor is the body of the class. The class name is followed by a parameter list, which are the constructor arguments. (As with any function, an empty parameter list may be omitted.)
class Foo(x: Int, y: String) { val xy: String = y * x /* now xy is a public member of the class */ } class Bar { ... }
The construction parameters of an instance are not accessible outside its constructor body unless marked as an instance member by the
val
keyword:class Baz(val z: String) // Baz has no other members or methods, so the body may be omitted val foo = new Foo(4, "ab") val baz = new Baz("I am a baz") foo.x // will not compile: x is not a member of Foo foo.xy // returns "abababab": xy is a member of Foo baz.z // returns "I am a baz": z is a member of Baz val bar0 = new Bar val bar1 = new Bar() // Constructor parentheses are optional here
Any operations that should be performed when an instance of an object is instantiated are written directly in the body of the class:
class DatabaseConnection (host: String, port: Int, username: String, password: String) { /* first connect to the DB, or throw an exception */ private val driver = new AwesomeDB.Driver() driver.connect(host, port, username, password) def isConnected: Boolean = driver.isConnected ... }
Note that it is considered good practice to put as few side effects into the constructor as possible; instead of the above code, one should consider having
connect
anddisconnect
methods so that consumer code is responsible for scheduling IO.Auxiliary Constructors
A class may have additional constructors called 'auxiliary constructers'. These are defined by constructor definitions in the form
def this(...) = e
, wheree
must invoke another constructor:class Person(val fullName: String) { def this(firstName: String, lastName: String) = this(s"$firstName $lastName") } // usage: new Person("Grace Hopper").fullName // returns Grace Hopper new Person("Grace", "Hopper").fullName // returns Grace Hopper
This implies each constructor can have a different modifier: only some may be available publicly:
class Person private(val fullName: String) { def this(firstName: String, lastName: String) = this(s"$firstName $lastName") } new Person("Ada Lovelace") // won't compile new Person("Ada", "Lovelace") // compiles
In this way you can control how consumer code may instantiate the class.
-
-
Type check:
variable.isInstanceOf[Type]
With pattern matching (not so useful in this form):
variable match { case _: Type => true case _ => false }
Both
isInstanceOf
and pattern matching are checking only the object's type, not its generic parameter (no type reification), except for arrays:val list: List[Any] = List(1, 2, 3) //> list : List[Any] = List(1, 2, 3) val upcasting = list.isInstanceOf[Seq[Int]] //> upcasting : Boolean = true val shouldBeFalse = list.isInstanceOf[List[String]] //> shouldBeFalse : Boolean = true
But
val chSeqArray: Array[CharSequence] = Array("a") //> chSeqArray : Array[CharSequence] = Array(a) val correctlyReified = chSeqArray.isInstanceOf[Array[String]] //> correctlyReified : Boolean = false val stringIsACharSequence: CharSequence = "" //> stringIsACharSequence : CharSequence = "" val sArray = Array("a") //> sArray : Array[String] = Array(a) val correctlyReified = sArray.isInstanceOf[Array[String]] //> correctlyReified : Boolean = true //val arraysAreInvariantInScala: Array[CharSequence] = sArray //Error: type mismatch; found : Array[String] required: Array[CharSequence] //Note: String <: CharSequence, but class Array is invariant in type T. //You may wish to investigate a wildcard type such as `_ <: CharSequence`. (SLS 3.2.10) //Workaround: val arraysAreInvariantInScala: Array[_ <: CharSequence] = sArray //> arraysAreInvariantInScala : Array[_ <: CharSequence] = Array(a) val arraysAreCovariantOnJVM = sArray.isInstanceOf[Array[CharSequence]] //> arraysAreCovariantOnJVM : Boolean = true
Type casting:
variable.asInstanceOf[Type]
With pattern matching:
variable match { case _: Type => true }
Examples:
val x = 3 //> x : Int = 3 x match { case _: Int => true//better: do something case _ => false } //> res0: Boolean = true x match { case _: java.lang.Integer => true//better: do something case _ => false } //> res1: Boolean = true x.isInstanceOf[Int] //> res2: Boolean = true //x.isInstanceOf[java.lang.Integer]//fruitless type test: a value of type Int cannot also be a Integer trait Valuable { def value: Int} case class V(val value: Int) extends Valuable val y: Valuable = V(3) //> y : Valuable = V(3) y.isInstanceOf[V] //> res3: Boolean = true y.asInstanceOf[V] //> res4: V = V(3)
Remark: This is only about the behaviour on the JVM, on other platforms (JS, native) type casting/checking might behave differently.
-
A class in Scala is a 'blueprint' of a class instance. An instance contains the state and behavior as defined by that class. To declare a class:
class MyClass{} // curly braces are optional here as class body is empty
An instance can be instantiated using
new
keyword:var instance = new MyClass()
or:
var instance = new MyClass
Parentheses are optional in Scala for creating objects from a class that has a no-argument constructor. If a class constructor takes arguments:
class MyClass(arg : Int) // Class definition var instance = new MyClass(2) // Instance instantiation instance.arg // not allowed
Here
MyClass
requires oneInt
argument, which can only be used internally to the class.arg
cannot be accessed outsideMyClass
unless it is declared as a field:class MyClass(arg : Int){ val prop = arg // Class field declaration } var obj = new MyClass(2) obj.prop // legal statement
Alternatively it can be declared public in the constructor:
class MyClass(val arg : Int) // Class definition with arg declared public var instance = new MyClass(2) // Instance instantiation instance.arg //arg is now visible to clients
-
Let's say we have a class MyClass with no constructor argument:
class MyClass
In Scala we can instantiate it using below syntax:
val obj = new MyClass()
Or we can simply write:
val obj = new MyClass
But, if not paid attention, in some cases optional parenthesis may produce some unexpected behavior. Suppose we want to create a task that should run in a separate thread. Below is the sample code:
val newThread = new Thread { new Runnable { override def run(): Unit = { // perform task println("Performing task.") } } } newThread.start // prints no output
We may think that this sample code if executed will print
Performing task.
, but to our surprise, it won't print anything. Let's see what's happening here. If you pay a closer look, we have used curly braces{}
, right afternew Thread
. It created an annonymous class which extendsThread
:val newThread = new Thread { //creating anonymous class extending Thread }
And then in the body of this annonymous class, we defined our task (again creating an annonymous class implementing
Runnable
interface). So we might have thought that we usedpublic Thread(Runnable target)
constructor but in fact (by ignoring optional()
) we usedpublic Thread()
constructor with nothing defined in the body ofrun()
method. To rectify the problem, we need to use parenthesis instead of curly braces.val newThread = new Thread ( new Runnable { override def run(): Unit = { // perform task println("Performing task.") } } )
In other words, here
{}
and()
are not interchangeable. -
1Objects
Whereas Classes are more like blueprints, Objects are static (i.e. already instantiated):
object Dog { def bark: String = "Raf" } Dog.bark() // yields "Raf"
They are often used as a companion to a class, they allow you to write:
class Dog(val name: String) { } object Dog { def apply(name: String): Dog = new Dog(name) } val dog = Dog("Barky") // Object val dog = new Dog("Barky") // Class
Syntax
class MyClass{} // curly braces are optional here as class body is empty
class MyClassWithMethod {def method: MyClass = ???}
new MyClass() //Instantiate
object MyObject // Singleton object
class MyClassWithGenericParameters[V1, V2](vl: V1, i: Int, v2: V2)
class MyClassWithImplicitFieldCreation[V1](val v1: V1, val i: Int)
new MyClassWithGenericParameters(2.3, 4, 5)
or with a different type:new MyClassWithGenericParameters[Double, Any](2.3, 4, 5)
class MyClassWithProtectedConstructor protected[my.pack.age](s: String)
Sign up or log in
Save edit as a guest
Join Stack Overflow
Using Google
Using Facebook
Using Email and Password
We recognize you from another Stack Exchange Network site!
Join and Save Draft