Value classes

  • Type safety for no runtime cost
  • Broad view on Scala TypeSyste,

Domain Types

FILES :

  • DEMO : jug.lodz.workshops.starter.valueclasses.ValueClassesDemo

A Class based on primitives look like this :

class User(val id: Int, val age: Int,val salary: Int)

When creating new instance :

val user=new User(1,2,3)

It is difficult to say if we did not make a mistake by passing age when salary was expected. Compiler will not help us because everything is an Int

However we can easily create dedicated domain type for each variable

class UserId(val value: Int) extends AnyVal

class Age(val number: Int) extends AnyVal

class Salary(val amount: Int) extends AnyVal

class DomainUser(val id: UserId, val age: Age, val salary: Salary){
  override def toString = s"DomainUser(id=${id.value}, age=${age.number}, salary=${salary.amount})"
}

Now there is no risk that we pass wrong parameters into wrong methods spots.

val userId = new UserId(1)
val age = new Age(28)
val salary = new Salary(10000)

val domainUser = new DomainUser(userId, age, salary)

Let's see if we need to pay for this typesafety in the runtime

No allocation in runtime

We already saw code fragment which uses domain classes :

//primitive types
class User(val id: Int, val age: Int,val salary: Int)

//domain types
class UserId(val value: Int) extends AnyVal

class Age(val number: Int) extends AnyVal

class Salary(val amount: Int) extends AnyVal

class DomainUser(val id: UserId, val age: Age, val salary: Salary){
  override def toString = s"DomainUser(id=${id.value}, age=${age.number}, salary=${salary.amount})"
}

What we are going to see when we decompile result java classes? The compiled version of DomainUser constructor will expect 3 int like in the first example.

javap target/scala-2.12/classes/jug/lodz/workshops/starter/valueclasses/DomainUser.class 
Compiled from "ValueClassesDemo.scala"
public class jug.lodz.workshops.starter.valueclasses.DomainUser {
  public jug.lodz.workshops.starter.valueclasses.DomainUser(int, int, int);
}

pawel@maszyna:~/projects/workshops/jugworkshops$ javap target/scala-2.12/classes/jug/lodz/workshops/starter/valueclasses/User.class 
Compiled from "ValueClassesDemo.scala"
public class jug.lodz.workshops.starter.valueclasses.User {
  public jug.lodz.workshops.starter.valueclasses.User(int, int, int);

So we have type safety during compilation and primitive performance in runtime. The only cost is in compilation time and AnyVal restrictions like the one that it can have only one field.

Extension Methods

There is one additional usage of AnyVal . Because every class which extends AnyVal doesn't allocate additional memory so it is used very often when creating temporary implicit object for extension method

object Extensions{
  implicit class StringExtensions(base:String){
    def display=println(s"Extension : $base")
  }
}

import Extensions._
"Standard text".display

Types system

You can see on the picture that AnyVal is on the left side and that Every primitive representation inherit from it.

There are also two interesting types at the bottom of the picture. Those are called bottom types and fill holes of java system.

val a:Null=null
val i:AnyRef=a

Exercise

During those exercises we will recall some concepts learned in previous exercises.

  • EXERCISES : exercise file: starter.valueclasses.exercises.ValueClassesExercises tests: starter.valueclasses.exercises.ValueClassesExercisesTest
  • ANSWERS : starter.valueclasses.answers.ValueClassesAnswers

Exercise 1 - Database

In exercise file you have blueprint of three classes. Update those definitions so that all test from EXERCISE1 passes.

object Item {
  def apply(id:Int,name: String, description: String):Item = ???
}

object Amount {
  ??? // FIX
}

object Purchase {
  ??? //FIX
}

Also uncomment

object Database {

and check if database test passes.

Exercise 2 - Safe DAO

Update both methods findPurchase and findPurchaseWithItem in Safe Dao

Exercise 3 - Data Cache

Implement findPurchase so that results are stored in local cache (immutable Map with mutable reference)

results matching ""

    No results matching ""