05. Classes - RobertMakyla/scalaWiki GitHub Wiki

Class

In one file there might be multiple classes, and all of them are public by default, so no need to write 'public' Methods are functions or procedures in a class. Methods are public BY DEFAULT

class Counter {                        
    private var value = 0             // You MUST initialize the field

    def increment() { value += 1 }    // procedure (Methods)
    def current = value               // function  (Method)
}

using Class

val c = new Counter()     // or  val c = new Counter
c.increment()            // or   c increment()   or  c increment
c.current               // or    c current

If I declare function without parentheses "def current = ..." then I am forced to use myCounter.current without parentheses.

If I declare function with parentheses "def increment() ..." but without arguments then I can call it both ways.

Methods that changes state, should be declared with () even if it doesn't take parameters.

Getters and Setters added by Scala: getter: myCass.myField setter myCass.myField_=(newValue)

class Person {
    var age = 0 // public field by default
}              // for a private field, getter/setter are also private

getters and setters (interview)

When compiling a class, Scala creates getters and setters (Java methods):

For public variable :

public int age           // person.age - allowed ; person.age() - not allowed
public void age_$eq(int) // person.age_=(10)  or  person.age = 10

For public value :

public int age        // just getter

For private variable :

private int age
private void age_$eq(int)

For object-private variable/value :

// no getter or setter

Overriding Getters and setters - it's possible to make private fields visible outside class (interview)

class Person {
    private var privateAge = 0  // private getter/setter

    def age = privateAge      // overriding private getter
    def age_=(age: Int) {    // overriding private setter
        if (age> privateAge) privateAge = age; 
    }
}

Instead of 'age_=()' I could define more natural 'age()'. It's just another setter, which I can add.

However only 'age_=()' let me use 'person.age = 100' . The other let 'person.age(100)' or 'person age 100'

Creating custom functions instead of getters and setters (Encapsulation)

class Counter {
    private var value = 0

    def current = value
    def increment() { value += 1 }
}

To summarize (interview)

  1. var foo: Scala creates a **getter ** and a setter
  2. val foo: Scala creates a getter
  3. You can define custom methods foo and/or foo_=() which override the default getters/setters

Note: In Scala, you cannot have a write-only property (property with a setter and no getter).

Note: In Scala, when a field is private, the getter/setter (for variable) or just getter (for value) are also private. But I can override it to be public (as above)

Accessing private fields (interview)

In Scala as in Java, method can access private fields from other objects of the same class !!

class Counter {
    private var someVar = 0    // same as private[Counter]
                              // meaks that methods from Counter will have access to getter and setter

    def increment() { someVar += 1 }

    def isLess(other : Counter) = someVar < other.someVar 
  }      // it can access other.someVar as private field of other object

Object-Private fields (interview)

Note: If we wrote private[this] var value = 0 then accessing this private field from different object wouldn't be possible, even if it's the same class.

class Counter {
    private[this] var someVar = 0

    def increment() { someVar += 1 }
    def current = someVar   // necessary cause inLess() I cannot access other.someVar

    def isLess(other : Counter) = someVar < other.current
}

Note: For private var, scala generates private getter and setter

Note: For object-private var, scala does not generate getters or setters

Note: private[SomeClass] - means that methods from SomeClass will have access to this field

JavaBeans

Making scala generate JavaBean getters/setters (for Tools such as Spring/EJB)

import scala.reflect.BeanProperty
class Person {
    @BeanProperty var name: String = _  // or = null
}

This generates:

  1. name: String
  2. name_=(newValue: String): Unit
  3. getName(): String
  4. setName(newValue: String): Unit

Auxiliary (pomocnicze) Constructors (interview)

NOT REALLY SCALA WAY - too much of code - Primary Constructors are better, more concise

Auxiliary constructors are always called 'this'

Each auxiliary constructor MUST start with a call to a previous constructor (previous can be auxiliary or primary)

class Person {
    private var name = ""
    private var age = 0

    def this(name: String) { // First auxiliary constructor
      this()                 // MUST Call primary constructor - otherwise compilation error
      this.name = name       // cause it's a first auxiliary
    }
    def this(name: String, age: Int) { // Another auxiliary constructor
      this(name)                       // MUST Call any other constructor 
      this.age = age                   // (primary or auxiliary)
    }
}

new Person              // Primary constructor
new Person("Fred")      // First auxiliary constructor
new Person("Fred", 42)  // Second auxiliary constructor

Primary Constructor (SCALA WAY) (interview)

If there are parameters after the class name, then the class has a primary constructor with this args

If there are no parameters after the class name, then the class has a primary constructor with no parameters.

the primary constructor simply executes all statements in the body of the class

class Person(val name: String, val age: Int) {
    println("Executed by primary constructor")    

    def description = name + " is " + age + " years old"
}

Primary Constructor arguments (val name: String, var age: Int) are declaring fields and also methods:

  • name:String // no name_=(newName:String):Unit cause name is a value

  • age:Int

  • age_=(newAge:Int):Unit

    class Hi{ println("Hi was created")} ; val h = new Hi // "Hi was created"

    class Person(var age:Int = 29){ print("Person was created") age += 1 } val p = new Person // "Person was created" p.age // 30

Default values in primary constructor

class Person(val name: String = "Robert", val age: Int = 29)

val p = new Person              // Robert 29
val p = new Person("Robert",99) // Robert 99

val p = new Person(name = "Marko") // Marko  29
val p = new Person("Marko")        // Marko  29

val p = new Person(age = 99)    // Robert  99
val p = new Person(99)          // Error: Invalid first default argument

If we want to skip the name of arg on right side, all other args on left must be provided (with name or not)

Object-Private fields in primary constructor (interview)

class Person( name: String ) // this is OBJECT PRIVATE
    // declares fields:  private[this] val name: String
    //     if not used in this class, it's inaccessible from outside
    //     if used it in this class, it's read-only cause it's a val

class Person(private[this] val name: String ) {}    // the same

class Person(private[this] var name: String ) {}    // also possible 
    // it's read-write from inside of class

to sum-up (interview)

  • private val/var name: String

this gives PRIVATE field and PRIVATE SCALA getters (and setters for variable), it's exactly like private[MyClass]

  • val/var name: String

this gives PRIVATE field but PUBLIC SCALA getters (and setters for variable) (interview)

  • @BeanProperty val/var name: String

this gives PRIVATE field but PUBLIC SCALA getters and JavaBeans getters (and setters for variable)

Private primary constructor (interview)

class Person private(val id: Int) { ... }

Useful to mark that only companion object (the apply() method) can create an instance (interview)

That's because Companion Object can still call private constructor

Or to secure using constructor by DI framework (e.g. Google GUICE)

Requirements of class - throws IllegalArgumentException (interview)

class Person(age:Int){
    require(age>=0 , "Age must be positive")
}

val p = new Person(-1)   // --> IllegalArgumentException

It's different that assert(Boolean) which throws AssertionError: assertion failed

Both functions: require(boolean, String) and assert(boolean) are predefined

Nesting

In scala I can nest classes in other classes, or functions in other functions.

In scala, inner class does not belong completely to outer class (unlike in Java) :

In scala: val inner = new myOuterClass.myInnerClass // so just adding outer class as prefix in name

In java: Object inner = myOuterClassObject.new myInnerClass() // need object of outer class to create/access inner