I am using case classes for models in an ORM. Each model has an id, but the id shouldn't be publicly accessible. So I have a parent trait
trait WithId {
private var id: Long = 0
}
and a lot of case classes (the models) inheriting from it
case class C1(a: Int, b: String) extends WithId
case class C2(...) extends WithId
...
Now, if someone calls copy() on a case class, it doesn't copy the id with it, but sets it to 0.
val c1 = C1(3, "bla")
//Set c1.id to a value != 0
val c2 = c1.copy(b="bla2")
//c1.id !=0, but c2.id = 0
I want it to copy the id as well.
Since I have a lot of these case classes, I'd prefer to have as few code as possible in the case classes themselves. So implementing a copy() method in each case class would be a lot of boilerplate code.
Is there a way to implement something in the trait that makes copy() also copy the id? Maybe something using macros? Or is there another way I didn't think about at all?
edit:
I could override the id field in each case class like
case class C1(a: Int, b: String, protected var id: Long)
Then it would be copied. But that also is boilerplate code I have to write per case class and it's hard to explain why you have to add an id field to a case class although you never notice it or can use it anywhere else when using the case class. I'd like to avoid that if possible.
If I were you, I'd add an ID-carrying token:
class IdToken private[myPackage] (val id: Int) {
override def equals(a: Any) = a match {
case tok: IdToken => id == tok.id
case _ => false
}
override def hashCode = id.hashCode ^ 0x157135
override def toString = ""
}
object IdToken {
private var lastId = 0
private[myPackage] def next = { lastId += 1; new IdToken(lastId) }
}
Since the constructor is private to your package, nobody else but you can create these tokens.
Then you write your trait as
trait WithID { protected def idToken: IdToken }
and
case class C1(a: Int, b: String, protected val idToken: IdToken = IdToken.next) extends WithID {
def checkId = idToken.id
}
(where you only need checkId for the test below) so you can
scala> C1(5, "fish")
res1: C1 = C1(5,fish,)
scala> res1.copy(a = 3)
res2: C1 = C1(3,fish,)
scala> res1.checkId == res2.checkId
res3: Boolean = true
But you can't access the token from outside code because the val is protected.
Hopefully that is good enough encapsulation.
Related
I have two case class Person and Employee
case class Person(identifier: String) {}
case class Employee (salary: Long) extends Person {}
I am getting following error:
Unspecified value parameters: identifier: String
Error: case class Employee has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes
I am new to Scala and not able to understand what I have to do.
Version:
Scala : 2.11
Unfortunately, I'm afraid it is not possible for case class to extend another case class.
The inheritance in "plain" classes would look like:
class Person(val identifier: String) {}
class Employee(override val identifier: String, salary: Long)
extends Person(identifier) {}
val el = new Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
If you would like to achieve a similar effect with case classes, you would need to reach out to traits:
trait Identifiable {
def identifier: String
}
case class Person(identifier: String) extends Identifiable {}
case class Employee(identifier: String, salary: Long)
extends Identifiable {}
val el = Employee("abc-test", 999)
println(el.identifier) // => "abc-test"
Defining extractors
Extractor provides a way for defining a matching statement used in pattern matching. It is defined in an object in unaply method.
Let's consider the first example again adding support for extractors:
class Person(val identifier: String)
class Employee(override val identifier: String, val salary: Long)
extends Person(identifier)
object Person {
def unapply(identifier: String): Option[Person] = {
if (identifier.startsWith("PER-")) {
Some(new Person(identifier))
}
else {
None
}
}
}
object Employee {
def unapply(identifier: String): Option[Employee] = {
if (identifier.startsWith("EMP-")) {
Some(new Employee(identifier, 999))
}
else {
None
}
}
}
Now, let's define a method that will define pattern matching using those extractors:
def process(anInput: String): Unit = {
anInput match {
case Employee(anEmployee) => println(s"Employee identified ${anEmployee.identifier}, $$${anEmployee.salary}")
case Person(aPerson) => println(s"Person identified ${aPerson.identifier}")
case _ => println("Was unable to identify anyone...")
}
}
process("PER-123-test") // => Person identified PER-123-test
process("EMP-321-test") // => Employee identified EMP-321-test, $999
process("Foo-Bar-Test") // => Was unable to identify anyone...
Case classes in Scala add several different features but often you really use only some of them. So the main question you need to answer is which features you really need. Here is a list based on the spec:
remove the need to type val before field names/constructor params
remove the need for new by adding apply method to the companion object
support for pattern matching by adding unapply method to the companion object. (One of nice things of Scala is that pattern-matching is done in a non-magical way, you can implement it for any data type without requiring it to be a case class)
add equals and hashCode implementations based on all the fields
add toString implementations
add copy method (useful because case classes are immutable by default)
implement Product trait
A reasonable guess of the equivalent for case class Person(identifier: String) is
class Person(val identifier: String) extends Product {
def canEqual(other: Any): Boolean = other.isInstanceOf[Person]
override def equals(other: Any): Boolean = other match {
case that: Person => (that canEqual this) && identifier == that.identifier
case _ => false
}
override def hashCode(): Int = identifier.hashCode
override def toString = s"Person($identifier)"
def copy(newIdentifier: String): Person = new Person(newIdentifier)
override def productElement(n: Int): Any = n match {
case 0 => identifier
case _ => throw new IndexOutOfBoundsException(s"Index $n is out of range")
}
override def productArity: Int = 1
}
object Person {
def apply(identifier: String): Person = new Person(identifier)
def unapply(person: Person): Option[String] = if (person eq null) None else Some(person.identifier)
}
case class Employee(override val identifier: String, salary: Long) extends Person(identifier) {}
Actually the main objections to inheriting from a case class and especially making a case class inheriting another one are the Product trait, copy and equals/hashCode because they introduce ambiguity. Adding canEqual partially mitigates the last problem but not the first ones. On the other hand in a hierarchy like yours, you probably don't need the copy method or Product implementation at all. If you don't use Person in pattern matching, you don't need unapply as well. Most probably all you really need case for is apply, toString and hashCode/equals/canEqual.
Inheriting from case classes (even with regular non-case classes, which is not prohibited) is a bad idea. Check this answer out to get an idea why.
You Person does not need to be a case class. It actually does not need to be a class at all:
trait Person {
def identifier: String
}
case class Employee(identifier: String, salary: Long) extends Person
//File Animal.scala
abstract class Animal {
val name: String
def getSomething(tClass: TypeClass): String = {
tClass.tName.split('.').lift(0)
}
def apply(tClass: TypeClass): SomeOtherClassType = {
// something...
}
// File: DogSpike, this is used for some specific cases (overwrites
base class val)
object DogSpike extends Animal {
override val name: String = "Spike"
}
this call then works (calls apply)
myTransformation(() => DogSpike(this))
Now I would like to create a more generic object that one can pass arguments but I am unable to.
It would work to create a derived Object from Animal that takes one arguments and being able to use the apply call
object TheDog(name: String) extends Animal {
override val name: String = "Spike"
//...
}
not sure how to implicitly call Animal.apply for TheDog object where I could pass a parameter (name)
myTransformation(() => TheDog(this))
// also this seems to be incorrect "*Wrong top statement declaration*"
object TheDog(val n: String) extends Animal {
override val name: String = n
//...
}
As of *Wrong top statement declaration* (I can understand only this part of your question) - you can't have constructor in object as object is a singleton, so you should use a case class (ADT):
final case class TheDog(name: String) extends Animal
scala>TheDog("Spike")
res2_2: TheDog = TheDog("Spike")
val and companion object with apply is added automatically for case classes, so you don't need to define your own own apply in Animal. case class TheDog(val name: String) is same as case class TheDog(name: String).
I's also use traits instead of abstract class:
trait Animal {
val name: String
def getSomething: String = {
"Dog: " + name
}
}
I don't understand your TypeClass type, but if you really want type classes:
trait Animal {
def name: String
}
final case class TheDog(name: String) extends Animal
final case class TheCat(name: String) extends Animal
implicit class RichDog(dog: TheDog){
def getSomething: String = {
"Dog" + dog.name
}
}
implicit class RichCat(cat: TheCat){
def getSomething: String = {
"Cat: " + cat.name
}
}
scala> TheDog("Spike").getSomething
res4_5: String = "DogSpike"
scala> TheCat("Tom").getSomething
res4_6: String = "Cat: Tom"
About calling apply "implicitly", I don't know why would anyone need this, but:
trait AnimalFactory[A <: Animal] {
def apply(name: String)(implicit constructor: String => A) = constructor(name)
}
object TheeeDog extends AnimalFactory[TheDog]
implicit def createDog(name: String) = TheDog(name)
TheeeDog("Spike")
Of course you have to provide createDog and make it visible for a client, but it doesn't really make sense if you can just use ADTs and define additional required applys in companion object:
case class TheMouse(name: String)
object TheMouse{
def apply(isJerry: Boolean): TheMouse = if (isJerry) TheMouse("Jerry") else TheMouse("NotJerry")
}
TheMouse(true)
If you want to add some parameter to constructor, just add it:
class AnimalFactory(clazz: SomeClass){
def doSomething = clazz.name
def apply(name: String)
}
val dogFactory = new AnimalFactory(dogClassDescriptor)
val catFactory = new AnimalFactory(catClassDescriptor)
dogFactory("Spike")
catFactory("Tom")
You can even create a factory for factory (I wouldn't recommend - this solution already looks overcomplicated):
object AnimalFactory{ //please don't use classes for that - avoiding `new` is not their purpose
def apply(clazz: SomeClass) = new AnimalFactory(clazz)
}
val dogFactory = AnimalFactory(dogClassDescriptor)
//or even `val spike = AnimalFactory(dogClassDescriptor)("Spike")`
But still what's the point if you could just provide underlying clazz either as a member or just in a wrapper:
final case class ClazzWrapper[T <: Animal](clazz: SomeClass, animal: T)
I know that we can overload class constructor in Scala as follows-
class Foo(x: Int, z: String) {
def this(z: String) = this(0, z);
}
But how can I overload a class which have two completely different types of parameters as below (imagine that I can identify an user either by name or numeric id)
class User(userId: Int) {
...
}
class User(userName: String) {
...
}
(imagine that I can identify an user either by name or numeric id)
You almost certainly don't want to do this by having optional fields in your class. Rather, you should encode the fact that the user is identified in various ways into the types and structure of your program.
One way to do this is to encode the user identifier using Scala's built-in Either type:
class User private(identifier : Either[String, Int]) {
def this(id : Int) = this(Right(id))
def this(name : String) = this(Left(name))
}
However, you might also want to make the nature of the user identifier a little more explicit, and encode it as your own Algebraic data type:
trait UserIdentifier
object UserIdentifier {
case class ById(id : Int) extends UserIdentifier
case class ByName(name : String) extends UserIdentifier
}
class User(id : UserIdentifier) {
def this(id : Int) = this(UserIdentifier.ById(id))
def this(name : String) = this(UserIdentifier.ByName(name))
}
By doing it this way, you prevent problems such as somebody trying to look up a name on a User which is identified by an id instead. The second approach also allows you to extend the idea of a UserIdentifier in the future, in case a user can be identified by some other construct.
Alternativelly, you can do this
object User {
def apply(userId: Int) = new UserI(userId)
def apply(name: String) = new UserS(name)
class UserI(userId: Int)
class UserS(userName: String)
}
And use it this way:
val u1 = User(1)
val u2 = User("a")
If you have a lot of common logic you can put it into a common abstract class
object User {
def apply(userId: Int) = new UserI(userId)
def apply(name: String) = new UserS(name)
class UserI(userId: Int) extends AUser
class UserS(userName: String) extends AUser
abstract class AUser{
// common logic for both classes
}
}
You can do this:
class User private() {
def this( userName: String ) = { this(); ??? }
def this( userId: Int ) = { this(); ??? }
}
the private keyword makes the no-arg constructor private. This means your other secondary constructors don't need to pass anything to the primary constructor
(effectively making the two secondary constructors independant), but the callers still cannot instantiate the class withouth passing any parameter.
Note that this pattern can be tricky to use when your class has vals to initialize from the construtors parameters.
In Scala I need to do something like following code in Java does:
public class A {
private String text;
public A(String text) {
this.text = text;
}
}
How can achieve that in Scala?
I know that I can use class A(text: String) { ... }, but this is only a very simplified example and not real case.
I have tried the following and it prints always null:
class A {
var text: String = null
def this(text: String) = {
this()
this.text = text
}
println(text)
}
Thanks for help.
In Scala classes have only one main constructor, and it's exactly what you define like this: class A(text: String) { ... }. All other constructors should call it at first and then do their own stuff.
All expressions in the body of the class are treated as body of the main constructor. So println(text) in you case is in the body of the main constructor. You call this() in your def this(text: String) constructor at first, so println is executed and only then you initialize text field.
From my point of view, main constructor can satisfy most of the real-life use-cases (especially with default and named arguments). So can you please elaborate on this and explain why you need the second one? Maybe we can find beter solution for the problem you are facing there.
Update regarding the comment
If you want to provide a set of optional arguments during construction time, then I recommend you to use Option instead of null values. So you can implement your A class like this:
class A(val a: Option[String] = None, val b: Option[Int] = None, c: Option[Double] = Some(2.0))
new A(b = Some(10))
All fields are now constant and have some default, but you can customize some of them. It's also possible that some combination of arguments are mutually exclusive, according to some business logic, for example. In this case it's reasonable to use several constructors. But even better solution can be to create several factory methods in companion object of the class and make constructor private. For example, if users of the class are allowed to provide either a and b or c, then you can write something like this:
class A private (val a: Option[String] = None, val b: Option[Int] = None, c: Option[Double] = Some(2.0))
object A {
def apply(a: String, b: Int) = new A(Some(a), Some(b))
def apply(c: Double) = new A(c = Some(c))
}
A("test", 1)
A(11.1)
class A {
var text: String = null
def this(text: String) = {
this()
this.text = text
println(text)
}
}
or
class A(text:String=""){
println(text)
}
or
class A(text:String=null){
println(text)
}
The correct way to do that is :
class A( text : String) {
def this() = this("") // or null
}
As Tenshi said, the default constructor is the A(text : String) and all other constructors, for instance this() must call it first.
I am trying to write a generic method f[T](id:String) that is something like this:
case class A(x:String)
case class B(y:String)
case class C(z:String)
def f[T](id:String): T = { /* equivalent to T(id) */ }
val result1:A = f[A]("123") // returns A("123")
val result2:B = f{B]("345") // returns B("345")
val result3:C = f[C]("567") // returns C("567")
Unfortunately I cannot figure out how to work with the type T inside the method, besides using reflection. By "working with the type T" i mean for example being able to do something like the following, which I know doesn't work (for illustration purposes only):
T match {
case A => A(id)
case B => B(id)
}
or simply invoke T(ID) to create a new object of whatever type T is.
I can of course break up this into three methods:
def f1(id:String): A = { A(id) }
def f2(id:String): B = { B(id) }
def f3(id:String): C = { C(id) }
val result1:A = f1("123") // returns A("123")
val result2:B = f2("345") // returns B("345")
val result3:C = f3("567") // returns C("567")
but I'm hoping there is a way to keep it as one generic method to avoid some ugly boilerplate code duplication, and still be nearl as fast as the tree method version.
If you do not want to use reflection (ClassTag or TypeTag), you could use a Factory type class to achieve the desired functionality (unless it defeats the purpose of your generic function by generating a lot of duplicated simple code ;)).
case class A(s: String)
case class B(s: String)
case class C(s: String)
trait Factory[T] extends ((String) => T) {
def apply(arg: String): T
}
object Factory {
implicit object AFactory extends Factory[A] {
override def apply(arg: String): A = A(arg)
}
implicit object BFactory extends Factory[B] {
override def apply(arg: String): B = B(arg)
}
implicit object CFactory extends Factory[C] {
override def apply(arg: String): C = C(arg)
}
}
def create[T : Factory](arg: String): T = implicitly[Factory[T]].apply(arg)
create[A]("foo") | -> res0: A = A(foo)