We are no longer accepting contributions to Documentation. Please see our post on meta.

Scala Language

Classes and Objects All Versions

2.10.1
2.10.2
2.10.3
2.11.5
2.10.4
2.11.0
2.11.1
2.11.2
2.11.4
2.10.5
2.11.6
2.11.7
2.10.6
2.11.8
2.12.0
2.12.1
2.11.11
2.12.2

This draft deletes the entire topic.

Examples

  • 7

    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 named Factorial) 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 and class Factorial is its corresponding companion class. Companion objects and classes can access each other's private members. In the example above Factorial class is accessing the private cache 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.

  • 2

    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 and disconnect 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, where e 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.

  • 1

    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.

  • 1

    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 one Int argument, which can only be used internally to the class. arg cannot be accessed outside MyClass 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
    
  • 1

    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 after new Thread. It created an annonymous class which extends Thread:

    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 used public Thread(Runnable target) constructor but in fact (by ignoring optional ()) we used public Thread() constructor with nothing defined in the body of run() 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.

  • 1

    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
    
Please consider making a request to improve this example.

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)

Parameters

Parameters

Remarks

Remarks

Still have a question about Classes and Objects? Ask Question

Topic Outline


    We are no longer accepting contributions to Documentation. Drafts cannot be modified.