0

I have some code

case class A(s:String) {val x = "hello"}

Why can't I access the static variable x without instantiating the class A? If I type

A.x

I get the following error:

error: value x is not a member of object A

Edit:

I missed out mentioning the remaining code. Here is the example that I would like to use:

abstract class A { val name:String }
case class B(i:Int) extends A { val name = "B" }
case class C(b:Boolean) extends A { val name = "C" }
def getType(s:String) = s match {
  case B.name => println ("Object B")
  case C.name => println ("Object C")
}

The error:

  scala> def getType(s:String) = s match {
 |       case B.name => println ("Object B")
 |       case C.name => println ("Object C")
 |     }
 <console>:11: error: value name is not a member of object B
         case B.name => println ("Object B")
                ^
 <console>:12: error: value name is not a member of object C
         case C.name => println ("Object C")
                ^

As to why use case classes, the case classes are not defined for this purpose. Elsewhere I have some code like:

def func(a:A) = a match {
  case b:B =>
  case c:C =>
  ...
}
Jus12
  • 17,824
  • 28
  • 99
  • 157
  • 4
    It is not static. You'd need to make it a member of the companion object for it to be static. – Tom Crockett Jan 28 '11 at 05:47
  • 1
    This is not convenient because I have hundreds of case classes and would need to add a companion object to each of them. Is this the only way? – Jus12 Jan 28 '11 at 06:19
  • 1
    It may be the only way to do what you are describing. Why not give as a slightly wider context? Some small redesign will most likely solve your problem elegantly. – Synesso Jan 28 '11 at 06:23
  • 1
    Do these case classes extend a common parent? If so, put it in the parent's companion. – Tom Crockett Jan 28 '11 at 06:26
  • Are you sure hundreds of case classes are a good design in your circumstances? That is, if you generate the code you haven no problem, and if you write it by hand, wow, what are you doing? – Raphael Jan 28 '11 at 09:07
  • @pelotom There is no such thing as a static member in Scala. – Raphael Jan 28 '11 at 09:08
  • Can you give a better idea of the bigger problem you're trying to solve here. case classes really don't seem to be the appropriate tool for what you're describing. – Kevin Wright Jan 28 '11 at 10:02
  • @Jus12 if you have hundreds of case classes, do you have hundreds of places calling these methods? That seems very unlikely to me, and a bit pointless. – Daniel C. Sobral Jan 28 '11 at 11:24
  • There are not hundreds, but around 80 of these case classes. Each case class, say `Case1` is the subclasses of an abstract class, say, `Abs`. I define an uninitialized `val x:String` in `Abs`, and all subclasses need to initialize this `x`. Later on, given a string `s`, I want to do `s match { case Case1.x => ... }`, which does not seem to work. – Jus12 Jan 28 '11 at 12:08
  • You know that you do not need case classes in order to use pattern matching, right? Right?! (hint: `unapply`) – Raphael Jan 30 '11 at 20:18

3 Answers3

5

Well, you cannot call the "static" variable x, because in Scala there are no static variables. You are declaring x to be a regular member of class A, which you could access if you had an instance of class A.

What you try to do by calling A.x is accessing a value with the name "A". There happens to be such a value in scope - the compiler generated companion object for your case class A.

But this object A has no member "x", therefore the compiler rightly complains about it.

You can add the value x to the object A instead of the class/type A by doing the following:

case class A(s:String)
object A { val x = "hello" }
Ruediger Keller
  • 3,024
  • 2
  • 20
  • 17
  • The problem with this approach: If you declare a custom companion object for case classes the compiler won't add some utility methods, e.g. `tupled` is missing afterwards. See this question https://stackoverflow.com/q/25392422/935676 – amoebe Dec 03 '17 at 10:55
3

From the small amount you described of the problem, it sounds like case classes are just not for you. Alternate patterns include...

Constants:

val Abs1 = "1" //note that it starts with an uppercase letter
val s: String = ...
s match {
  case Abs1 => ...
  case _ =>
}

Extractors:

object Positive {
  def unapply(i: Int): Option[Int] = if(i >= 0) Some(i) else None
}

val i: Int = ...
i match {
  case Positive(p) => ... //p will be bound to the matched positive number
  case _ => ...
}

Case Classes (used properly):

case class MyType(s: String)
val x: MyType = ...

x match {
  case MyType("a") => ...
  case MyType("b") => ...
  case MyType(matched) => ...
    //matched will be bound to whatever string was used to construct the MyType instance
}

Case Objects:

abstract sealed trait Foo { def s: String }
case object Bar extends Foo { val s = "I'm a Bar" }
case object Baz extends Foo { val s = "I'm a Baz" }

val x: Foo = ...
x match {
  case Bar => ...
  case Baz => ...
  //no other possibilities because Foo is sealed
}
Kevin Wright
  • 49,540
  • 9
  • 105
  • 155
2

Leaving aside issues of design for a moment.

If you need a workaround then you can bind an identifier to a matched case class or case object and use the bound identifier to access members of the matched entity. The bound identifier can be used in guard statements in the match or in the action provided for the match:

case class Bob(x: String, y: String) {val z = "Bragging"}

val bob1 = Bob("Big", "Bad")

bob1 match {
  case b @ Bob(x, y) if b.z == "Bragging" => println("Got "+x+", "+y+", "+b.z+" Bob!")
  case _ =>
}

case object Bob {val z = "Balding"}

val bob2 = Bob

bob2 match {
  case b @ Bob if b.z == "Balding" => println("Got "+b.z+" Bob!")
  case _ =>
}

Returning to design, in your case class definition you declare 'name' in the constructor body of B but you would get more useability from having 'name' as a parameter:

case class B(i: Int, name: String) extends A

Which could then match like this:

def getType(s:String) = s match {
  case B(_, name) => println ("Object B("+name+")")
  ...

Finally it's hard to say without further detail but I suspect that mapping case classes to a large set of similar entities on a one to one basis is perhaps not the best choice, better to use case objects, or instances of a limited number of case classes or even tuples.

Don Mackenzie
  • 7,953
  • 7
  • 31
  • 32
  • I don't prefer the solution `case class B(i: Int, name: String) extends A` because in my design `B(1) and B(2)` need to be enforced to have `name = "Object B"`, while putting this as a parameter does not enforce it. – Jus12 Jan 28 '11 at 20:33
  • 1
    If 'name' doesn't change across instances of a case class then perhaps it's best to consider putting name in the companion object for the class or for a common base class as suggested by @pelotom. The parameter idea is about making pattern matching and extraction easier. – Don Mackenzie Jan 28 '11 at 23:11