I have big object:
case class BigObject(val str: String, val number: Int) {
val someVal = ...
val someVal2 = ...
}
I'd like to copy this object without re-valuation of values. Is it possible? Right now I'm using this approach:
val newBigObject = oldBigObject.copy(str = newStr)
As I see from the logs/debugger, "someVal" and "someVal2" are re-valuated. Is it possible to avoid it? As my BigObject is really big and value re-valuation takes some time, but performance is really important for me.
Thanks for your answers!
Here's a way:
Make the someVal and someVal2 fields which are also passed to the constructor and pull out the initialization logic for those fields in the companion object.
In your case:
class BigObject private(val str: String,
val number: Int,
val someVal: SomeType,
val someVal2: SomeType) {
def copy(newStr: String = str, newNumber: Int = number) = {
new BigObject(newStr, newNumber, someVal, someVal2)
}
}
object BigObject {
def apply(str: String, number: Int): BigObject = {
val someVal = initialize() //your initialization logic here
val someVal2 = initialize2()
new BigObject(str, number, someVal, someVal2)
}
}
Now, you can copy without re-evaling the inner fields:
val bigObj = BigObject("hello", 42)
val anotherBigObj = bigObj.copy(newStr = "anotherStr")
Alternatively, if you don't like companion objects, you can make two constructors. The primary one includes all the fields (also the non visible ones) and will be private. The public one will have only the two visible parameters:
class BigObject private(val str: String,
val number: Int,
val someVal: Any,
val someVal2: Any) {
def this(str: String, number: Int) = this(str, number, initializeVal, initializeVal2)
def copy(newStr: String = str, newNumber: Int = number) = {
new BigObject(newStr, newNumber, someVal, someVal2)
}
}
Usage:
val bigObj = new BigObject("hello", 42)
val anotherBigObj = bigObj.copy(newStr = "anotherStr")
Related
class Person {
val studentName = "Arpana"
def changeName(id:String, name:String) ={
val studentName = name
useName(id)
}
def useName(id:String) = {
println(s"use name is $id, by $studentName")
}
}
object Person {
def main(args: Array[String]): Unit = {
(new Person).changeName("2", "Shubham")
}
}
I don't want to use var in code, can we do it by keywords, I tried with keywords like super, protected, private, final but didn't work.
In actual I want to apply this in the below code.
abstract class BaseRepository[T <: BaseModel : ClassTag : StriveSerializer] {
self: BaseConnection =>
val tableName: String = implicitly[ClassTag[T]].runtimeClass.getSimpleName
private val serializer = implicitly[StriveSerializer[T]]
private def executeInserts(query: String): Future[Boolean] = Future {
val preparedStatement = self.connection.prepareStatement(query)
preparedStatement.execute()
}
def exist(id: String, name: String): Future[Boolean] = {
val tableName = name
val promise = Promise[Boolean]
queryById(id).onComplete {
case Success(_) => promise.success(true)
case Failure(ex) => promise.failure(ex)
}
promise.future
}
def queryById(id: String): Future[T] = {
val getSql = s"SELECT * FROM $tableName WHERE id == $id;"
executeReads(getSql).map(serializer.fromResultSet)
}
}
I want when i call exist function then table name given in exist function override in queryById method table name .
It seems like a bit of mix of Java and Scala style. I tried to refactor a bit assuming the intention behind the code. Try and see if this achieves what you want to do:
class Person(_id: String, _studentName: String) {
private val id: String = _id
private val studentName: String = _studentName
def useName() = {
println(s"use name is $id, by $studentName")
}
}
object Person extends App {
new Person("2", "Shubham").useName()
}
I think you should use case class For Model
case class Student(id:String,name:String)
def changeId(student:Student, newId:String): Student ={
student.copy(id=newId)
}
val s1 = Student("1","A")
val newS1 = changeId(s1,"2")
I think it okay to use mutable in a class
e.g.
class MySuperService{
var lastHeartbeat: Option[Timestamp] = None
def setLastHeartbeat(ts:Timestamp): Unit ={
lastHeartbeat = Some(ts)
}
}
val mss1 = new MySuperService()
mss1.setLastHeartbeat(???)
I have created a class Path which should be uniquely identifiable by occupation and last.
case class Path(occupation: BitSet, last: Int) {
var cost = 0
def setCost(cost: Int) {
this.cost = cost
}
def getCost(): Int = {
return cost
}
}
}
Also, I would like it to be sortable by count, which I made a field.
implicit val pathOrd = Ordering.by((p: Path) => p.getCost)
The problem is that when I am sorting it(as you can see in the line above), I get java.lang.NullPointerException on that line.
Why is this happening?
Can I store my data better?
Your code gives no exceptions to me using this code:
# class Path(occupation: BitSet, last: Int) {
var cost = 0
def setCost(cost: Int) {
this.cost = cost
}
def getCost(): Int = {
return cost
}
}
defined class Path
# List(new Path(BitSet(), 3))
res6: List[Path] = List(cmd5$Path#3ef8de39)
# implicit val pathOrd = Ordering.by((p: Path) => p.getCost)
pathOrd: Ordering[Path] = scala.math.Ordering$$anon$9#5f478e42
# res6.sorted
res9: List[Path] = List(cmd5$Path#3ef8de39)
You are missing val for occupation and last
class Path(val occupation: BitSet, val last: Int)
I would suggest you to create a case class
case class Path(occupation: BitSet, last: Int)
It will have equals, and hashCode based on it's fields, toString, apply, unapply and copy methods.
I am not sure if you really need to modify the cost, if it is calculated based on other values you could make it a method
case class Path(occupation: BitSet, last: Int) {
def cost: Int = 42
}
if it's not then it should be a field. I want to encourage you to use immutable structures, which would mean doing:
case class Path(occupation: BitSet, last: Int, cost: Int)
Adding a field with setter and getter in scala is as simple as this:
class Path(val occupation: BitSet, val last: Int) {
var cost = 0
}
You can use it like this:
val path = new Path(BitSet(), 3)
path.cost = 12
println(path.cost)
Here is a simpler version of the code that uses Scala's conversions for using var to indicate mutable state in a class:
case class Path
(occupation: BitSet, // Immutable (will never change)
var cost: Int = 0, // Mutable
var last: Int = 0 // Mutable
)
// Implementing the Ordering TypeClass
implicit val pathOrd = Ordering.by((p: Path) => p.cost)
val t = new Path(BitSet(1,2), 0, 0)
t.cost = 2 // changing cost
val data = Seq(new Path(BitSet(1,2), 0, 0), new Path(BitSet(1), 3, 0), new Path(BitSet(1), 2, 0))
println(data.sorted)
Is it possible to do that in Scala using only val:
class MyClass {
private val myVal1: MyClass2 //.....????? what should be here?
def myMethod1(param1: Int) = {
myVal1 = new MyClass2(param1)
//....
// some code....
}
}
The idea is that I can't initialize myVal1 immediately since the argument for its constructor is unknown yet and I have to do that in myMethod1. myVal1 should be visible withing a class and should be immutable.
No mutable state allowed.
So is it possible?
No, it isn't possible to do in the way you want. Consider, what would be the result of
val mc = new MyClass
mc.method1(0)
mc.method1(1)
? An exception thrown for setting myVal1 twice? Or should it keep the first value?
This is not possible, but there are some ways (in addition to using param1 as a constructor parameter)
Change the var into an Option; the setter myMethod1 returns a new instance of the same class with the Option set to the value.
Create a separate mutable Builder class with a var, and turn it into an immutable one later, when all data has been collected
If you are dealing with forward or cyclic references, consider using call-by-name and lazy vals (example 1, example 2)
Update: Example for 1:
class MyClass(val myVal1: Option[Int]) {
def myMethod1(param1: Int): MyClass = {
new MyClass(Some(param1))
}
}
object MyClass {
def apply() = new MyClass(None)
def apply(i: Int) = new MyClass(Some(i))
}
This pattern is used by immutable.Queue for example.
Update: Example for 3 (cyclic reference):
// ref ... call by name
class MyClass(val id: Int, ref: => MyClass) {
lazy val myVal1 = ref
override def toString: String = s"$id -> ${myVal1.id}"
}
to be used like this:
val a: MyClass = new MyClass(1, b)
val b: MyClass = new MyClass(2, a)
println(a)
println(b)
Update: Example for 3 (forward reference):
class MyClass2(val id: Int)
// ref ... call by name
class MyClass(val id: Int, ref: => MyClass2) {
lazy val myVal1 = ref
override def toString: String = s"$id -> ${myVal1.id}"
}
to be used with
val a = new MyClass(1, x)
println(a.id) // You can use a.id, but not yet the lazy val
val x = new MyClass2(10)
println(a)
To imitate a lazy "value" whose initial value might not be retrieved until after instance initialization completes (btw, there is nothing special about such objects, e.g. Swift have lazy properties that are even recommended to be declared as variables), you can introduce a wrapper to repeat the same logic that the Scala compiler generates internally for lazy values in Scala:
class LazyVar[T] {
private[this] var value$compute: () => T = () => null.asInstanceOf[T]
#volatile private[this] var value$: T = null.asInstanceOf[T]
#volatile private[this] var isInitialized$ = false
#volatile private[this] var isComputed$ = false
def value_=(value: T) = this.synchronized {
if(!isInitialized$) {
value$compute = () => value
isInitialized$ = true
}
else throw new IllegalStateException("Already initialized")
}
def value: T = this.synchronized {
if(!isInitialized$) throw new IllegalStateException("Not yet initialized")
else if(isComputed$) value$
else {
value$ = value$compute()
isComputed$ = true
value$
}
}
}
Now you just have to change MyClass2 to LazyVar[MyClass2] keeping tha val keyword as you wanted:
case class MyClass2(param: Int)
class MyClass {
private val myVal1: LazyVar[MyClass2] = new LazyVar[MyClass2]
def this(param: Int) {
this()
println("Storing the result of an expensive function...")
myVal1.value = new MyClass2(param)
}
def debug() = println(myVal1.value)
}
Now, if you write something like
val myClass = new MyClass(42)
myClass.debug
myClass.debug
you'll see the value is only computed once:
Storing the result of an expensive function...
MyClass2(42)
MyClass2(42)
Is it possible to do that in Scala using only val:
class MyClass {
private val myVal1: MyClass2 //.....????? what should be here?
def myMethod1(param1: Int) = {
myVal1 = new MyClass2(param1)
//....
// some code....
}
}
The idea is that I can't initialize myVal1 immediately since the argument for its constructor is unknown yet and I have to do that in myMethod1. myVal1 should be visible withing a class and should be immutable.
No mutable state allowed.
So is it possible?
No, it isn't possible to do in the way you want. Consider, what would be the result of
val mc = new MyClass
mc.method1(0)
mc.method1(1)
? An exception thrown for setting myVal1 twice? Or should it keep the first value?
This is not possible, but there are some ways (in addition to using param1 as a constructor parameter)
Change the var into an Option; the setter myMethod1 returns a new instance of the same class with the Option set to the value.
Create a separate mutable Builder class with a var, and turn it into an immutable one later, when all data has been collected
If you are dealing with forward or cyclic references, consider using call-by-name and lazy vals (example 1, example 2)
Update: Example for 1:
class MyClass(val myVal1: Option[Int]) {
def myMethod1(param1: Int): MyClass = {
new MyClass(Some(param1))
}
}
object MyClass {
def apply() = new MyClass(None)
def apply(i: Int) = new MyClass(Some(i))
}
This pattern is used by immutable.Queue for example.
Update: Example for 3 (cyclic reference):
// ref ... call by name
class MyClass(val id: Int, ref: => MyClass) {
lazy val myVal1 = ref
override def toString: String = s"$id -> ${myVal1.id}"
}
to be used like this:
val a: MyClass = new MyClass(1, b)
val b: MyClass = new MyClass(2, a)
println(a)
println(b)
Update: Example for 3 (forward reference):
class MyClass2(val id: Int)
// ref ... call by name
class MyClass(val id: Int, ref: => MyClass2) {
lazy val myVal1 = ref
override def toString: String = s"$id -> ${myVal1.id}"
}
to be used with
val a = new MyClass(1, x)
println(a.id) // You can use a.id, but not yet the lazy val
val x = new MyClass2(10)
println(a)
To imitate a lazy "value" whose initial value might not be retrieved until after instance initialization completes (btw, there is nothing special about such objects, e.g. Swift have lazy properties that are even recommended to be declared as variables), you can introduce a wrapper to repeat the same logic that the Scala compiler generates internally for lazy values in Scala:
class LazyVar[T] {
private[this] var value$compute: () => T = () => null.asInstanceOf[T]
#volatile private[this] var value$: T = null.asInstanceOf[T]
#volatile private[this] var isInitialized$ = false
#volatile private[this] var isComputed$ = false
def value_=(value: T) = this.synchronized {
if(!isInitialized$) {
value$compute = () => value
isInitialized$ = true
}
else throw new IllegalStateException("Already initialized")
}
def value: T = this.synchronized {
if(!isInitialized$) throw new IllegalStateException("Not yet initialized")
else if(isComputed$) value$
else {
value$ = value$compute()
isComputed$ = true
value$
}
}
}
Now you just have to change MyClass2 to LazyVar[MyClass2] keeping tha val keyword as you wanted:
case class MyClass2(param: Int)
class MyClass {
private val myVal1: LazyVar[MyClass2] = new LazyVar[MyClass2]
def this(param: Int) {
this()
println("Storing the result of an expensive function...")
myVal1.value = new MyClass2(param)
}
def debug() = println(myVal1.value)
}
Now, if you write something like
val myClass = new MyClass(42)
myClass.debug
myClass.debug
you'll see the value is only computed once:
Storing the result of an expensive function...
MyClass2(42)
MyClass2(42)
If you have a case class like:
case class Foo(x: String, y: String, z: String)
And you have two instances like:
Foo("x1","y1","z1")
Foo("x2","y2","z2")
Is it possible to merge instance 1 in instance 2, except for field z, so that the result would be:
Foo("x1","y1","z2")
My usecase is just that I give JSON objects to a Backbone app through a Scala API, and the Backbone app gives me back a JSON of the same structure so that I can save/update it. These JSON objects are parsed as case class for easy Scala manipulation. But some fields should never be updated by the client side (like creationDate). For now I'm doing a manual merge but I'd like a more generic solution, a bit like an enhanced copy function.
What I'd like is something like this:
instanceFromDB.updateWith(instanceFromBackbone, excludeFields = "creationDate" )
But I'd like it to be typesafe :)
Edit:
My case class have a lot more fields and I'd like the default bevavior to merge fields unless I explicitly say to not merge them.
What you want is already there; you just need to approach the problem the other way.
case class Bar(x: String, y: String)
val b1 = Bar("old", "tired")
val b2 = Bar("new", "fresh")
If you want everything in b2 not specifically mentioned, you should copy from b2; anything from b1 you want to keep you can mention explicitly:
def keepY(b1: Bar, b2: Bar) = b2.copy(y = b1.y)
scala> keepY(b1, b2)
res1: Bar = Bar(new,tired)
As long as you are copying between two instances of the same case class, and the fields are immutable like they are by default, this will do what you want.
case class Foo(x: String, y: String, z: String)
Foo("old_x", "old_y", "old_z")
// res0: Foo = Foo(old_x,old_y,old_z)
Foo("new_x", "new_y", "new_z")
// res1: Foo = Foo(new_x,new_y,new_z)
// use copy() ...
res0.copy(res1.x, res1.y)
// res2: Foo = Foo(new_x,new_y,old_z)
// ... with by-name parameters
res0.copy(y = res1.y)
// res3: Foo = Foo(old_x,new_y,old_z)
You can exclude class params from automatic copying by the copy method by currying:
case class Person(name: String, age: Int)(val create: Long, val id: Int)
This makes it clear which are ordinary value fields which the client sets and which are special fields. You can't accidentally forget to supply a special field.
For the use case of taking the value fields from one instance and the special fields from another, by reflectively invoking copy with either default args or the special members of the original:
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
import System.{ currentTimeMillis => now }
case class Person(name: String, age: Int = 18)(val create: Long = now, val id: Int = Person.nextId) {
require(name != null)
require(age >= 18)
}
object Person {
private val ns = new java.util.concurrent.atomic.AtomicInteger
def nextId = ns.getAndIncrement()
}
object Test extends App {
/** Copy of value with non-defaulting args from model. */
implicit class Copier[A: ClassTag : TypeTag](val value: A) {
def copyFrom(model: A): A = {
val valueMirror = cm reflect value
val modelMirror = cm reflect model
val name = "copy"
val copy = (typeOf[A] member TermName(name)).asMethod
// either defarg or default val for type of p
def valueFor(p: Symbol, i: Int): Any = {
val defarg = typeOf[A] member TermName(s"$name$$default$$${i+1}")
if (defarg != NoSymbol) {
println(s"default $defarg")
(valueMirror reflectMethod defarg.asMethod)()
} else {
println(s"def val for $p")
val pmethod = typeOf[A] member p.name
if (pmethod != NoSymbol) (modelMirror reflectMethod pmethod.asMethod)()
else throw new RuntimeException("No $p on model")
}
}
val args = (for (ps <- copy.paramss; p <- ps) yield p).zipWithIndex map (p => valueFor(p._1,p._2))
(valueMirror reflectMethod copy)(args: _*).asInstanceOf[A]
}
}
val customer = Person("Bob")()
val updated = Person("Bobby", 37)(id = -1)
val merged = updated.copyFrom(customer)
assert(merged.create == customer.create)
assert(merged.id == customer.id)
}
case class Foo(x: String, y: String, z: String)
val foo1 = Foo("x1", "y1", "z1")
val foo2 = Foo("x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("x1", "y1", "z2")
If you change Foo later to:
case class Foo(w: String, x: String, y: String, z: String)
No modification will have to be done. Explicitly:
val foo1 = Foo("w1", "x1", "y1", "z1")
val foo2 = Foo("w2", "x2", "y2", "z2")
val mergedFoo = foo1.copy(z = foo2.z) // Foo("w1", "x1", "y1", "z2")