Search code examples
scalaakkatypechecking

Case-classes and its default constructors


Suppose, I defined the case class:

case class A(id: Int)

Then I suddenly typed this code:

val a = A

The question is - why compiler allows such writings? In Java, defining constructor with any parameters, makes the default constructor not available.

EDIT: Ok, as Ende Neu pointed, the a now contains A.type. How can I avoid scenario, when suddenly typing a = A may break my code in:

a match {
   case A(id) => do something
   case _ => I didn't expect fall into here
}

I'm trying to avoid to fall into the second case in my Akka message processing code. Is it possible?

UPDATE: After some time spent on working with Akka + Scala I figured out, the answer to the second part of this question. Actor's receive method has signature PartialFunction[Any, Unit], that means there's no typecheck for the input parameter. So any type is accepted, including A.type. Best advice to the user in this context: be careful when sending the messages to actors.


Solution

  • You are assigning A.type to a:

    scala> case class A(id: Int)
    defined class A
    
    scala> val a = A
    a: A.type = A
    
    scala> a()
    <console>:11: error: not enough arguments for method apply: (id: Int)A in object A.
    Unspecified value parameter id.
                a()
                 ^
    
    scala> a(1)
    res1: A = A(1)
    

    After the assignment a is of type A.type, then you can use a to construct new instances of the case class A, but you will still need to specify the id parameter.

    Regarding your edit:

    a match {
      case A(id) => do something
      case _ => I didn't expect fall into here
    }
    

    I think this correctly throws an exception in this case since you wanted a variable of type A and instead you ended up with a variable of type A.type, which is most probably a mistake, if you really want to differentiate between A and A.type in your match something like this should work:

    scala>  val a = A
    a: A.type = A
    
    scala>  a match {
         |    case a: A.type => "A.type"
         |    case someA: A => "case class"
         |    case _ => "default"
         |  }
    <console>:13: warning: fruitless type test: a value of type A.type cannot also be a A
                        case someA: A => "case class"
                                ^
    <console>:13: warning: unreachable code
                        case someA: A => "case class"
                                     ^
    res8: String = A.type
    
    scala> val someA = A(1)
    someA: A = A(1)
    
    scala>  someA match {
         |    case a: A.type => "A.type"
         |    case someA: A => "case class"
         |    case _ => "default"
         |  }
    <console>:12: warning: fruitless type test: a value of type A cannot also be a A.type
                        case a: A.type => "A.type"
                             ^
    res9: String = case class
    

    But as I said, this feels weird, probably there's a better solution to this kind of problems that I don't know.