Non-value field is accessed in 'hashCode()' - scala

It happen with id. (Non-value field is accessed in 'hashCode()')
How can I fix it?
Here is my code:
import java.util.Date
case class Category() {
var id: Integer = _
var parentId: Integer = _
var name: String = _
var status: Boolean = _
var sortOrder: Integer = _
var createTime: Date = _
var updateTime: Date = _
def this(id: Integer, parentId: Integer, name: String, status: Boolean, sortOrder: Integer, createTime: Date, updateTime: Date) {
this()
this.id = id
this.parentId = parentId
this.name = name
this.status = status
this.sortOrder = sortOrder
this.createTime = createTime
this.updateTime = updateTime
}
override def hashCode(): Int = {
if (id != null) id.hashCode() else 0
}
}
If I change var id to val id, it will be ok. But I need setter, I cant't set it as a val.

Non value field in hashCode means you're relaying on a mutable field to generate the classes hashcode, which may be dangerous, depending how you use that class. For example, assume you have a HashMap[Category, String], you place an entry in the hash map and them mutate the id field, now that hashmap can no longer find the class instance by it's hashcode.
You're treating a case class like it's a regular, mutable Java class. Case classes provides the mechanism of encapsulating immutable data in a syntax convenient way (where you also get a few nice things for free, such as hashCode, equals, etc). What you can do is make your case class accept parameters via it's primary constructor:
case class Category(id: Int,
parentId: Int,
name: String,
status: Boolean,
sortOrder: Int,
createTime: Date,
updateTime: Date) {
override def hashCode(): Int = id.hashCode()
}
If you want to update Category, you can use the utility copy method defined on a case class:
val cat = Category(id = 1,
parentId = 1,
name = "Hello",
status = true,
sortOrder = 1,
createTime = new Date(123132123L),
updateTime = new Date(52234234L))
val newCat: Category = cat.copy(id = 2)
Another way of approaching this (which I like less TBH) would be to make all your fields an Option[T] with a default value set to None:
case class Category(id: Option[Int] = None,
parentId: Option[Int] = None,
name: Option[String] = None,
status: Option[Boolean] = None,
sortOrder: Option[Int] = None,
createTime: Option[Date] = None,
updateTime: Option[Date] = None) {
override def hashCode(): Int = id.map(_.hashCode()).getOrElse(0)
}
And then updating them once you can populate the class with data:
val cat = Category()
val newCat = cat.copy(id = Some(1))

After I try many times, I think this should be a good choice.
Any ideas?
override def hashCode(): Int = {
if (getId != null) getId.hashCode() else 0
}

Related

Calculate a derived column in the select output - Scala Slick 3.2.3

I am trying to write some REST API to fetch the data using Scala Slick 3.2.3. Is there a way to calculate a derived column and include it in the returned output?
My model:
case class Task(id: Option[TaskId], title: String, dueOn: String, status: String, createdAt: String, updatedAt: String)
Table class:
class TasksTable(tag: Tag) extends Table[Task](tag, _tableName = "TASKS") {
def id: Rep[TaskId] = column[TaskId]("ID", O.PrimaryKey, O.AutoInc)
def title: Rep[String] = column[String]("TITLE")
def dueOn: Rep[String] = column[String]("DUE_ON")
def status: Rep[String] = column[String]("STATUS")
def createdAt: Rep[String] = column[String]("CREATED_AT")
def updatedAt: Rep[String] = column[String]("UPDATED_AT")
def * = (id.?, title, dueOn, status, createdAt, updatedAt) <> ((Task.apply _).tupled, Task.unapply)
}
DAO:
object TasksDao extends BaseDao {
def findAll: Future[Seq[Task]] = tasksTable.result
}
I want to add a column in the response json called timeline with values "overdue", "today", "tomorrow", "upcoming", etc. calculated based on the dueOn value.
I tried searching but could not find any help. Any help with an example or any pointers would be highly appreciated. Thanks!
First I'd start from defining enum model for timeline:
object Timelines extends Enumeration {
type Timeline = Value
val Overdue: Timeline = Value("overdue")
val Today: Timeline = Value("today")
val Tomorrow: Timeline = Value("tomorrow")
val Upcoming: Timeline = Value("upcoming")
}
Then I'd modify dueOne column type from plain String to LocalDate - this will be easier to do on DAO level, so Slick will handle parsing errors for us.
So, to need to define custom type for LocalDate (see for more details: http://scala-slick.org/doc/3.0.0/userdefined.html#using-custom-scalar-types-in-queries).
// Define mapping between String and LocalDate
private val defaultDateFormat: DateTimeFormatter = DateTimeFormatter.ISO_DATE // replace it with formatter you use for a date
def stringDateColumnType(format: DateTimeFormatter): BaseColumnType[LocalDate] = {
MappedColumnType.base[LocalDate, String](_.format(format), LocalDate.parse(_, format))
}
implicit val defaultStringDateColumnType: BaseColumnType[LocalDate] = stringDateColumnType(defaultDateFormat)
private val defaultDateFormat: DateTimeFormatter = DateTimeFormatter.ISO_DATE // replace it with formatter you use for a date
// Change `dueOn` from String to LocalDate
case class Task(id: Option[TaskId], title: String, dueOn: LocalDate, status: String, createdAt: String, updatedAt: String)
class TasksTable(tag: Tag) extends Table[Task](tag, _tableName = "TASKS") {
def id: Rep[TaskId] = column[TaskId]("ID", O.PrimaryKey, O.AutoInc)
def title: Rep[String] = column[String]("TITLE")
def dueOn: Rep[LocalDate] = column[LocalDate]("DUE_ON") // Then replace column type
def status: Rep[String] = column[String]("STATUS")
def createdAt: Rep[String] = column[String]("CREATED_AT")
def updatedAt: Rep[String] = column[String]("UPDATED_AT")
def * = (id.?, title, dueOn, status, createdAt, updatedAt) <> ((Task.apply _).tupled, Task.unapply)
}
Then define API level model TaskResponse with new additional timeline field:
case class TaskResponse(id: Option[TaskId], title: String, dueOn: LocalDate, status: String, createdAt: String, updatedAt: String, timeline: Timeline)
object TaskResponse {
import Timelines._
def fromTask(task: Task): TaskResponse = {
val timeline = dueOnToTimeline(task.dueOn)
TaskResponse(task.id, task.title, task.dueOn, task.status, task.createdAt, task.updatedAt, timeline)
}
def dueOnToTimeline(dueOn: LocalDate): Timeline = {
val today = LocalDate.now()
Period.between(today, dueOn).getDays match {
case days if days < 0 => Overdue
case 0 => Today
case 1 => Tomorrow
case _ => Upcoming
}
}
}
Then you can create TasksService responsible for business logic of converting:
class TasksService(dao: TasksDao)(implicit ec: ExecutionContext) {
def findAll: Future[Seq[TaskResponse]] = {
dao.findAll.map(_.map(TaskResponse.fromTask))
}
}
Hope this helps!

in scala, how to create a list of immutable objects with references among them (when instances not know in advance)?

This answer https://stackoverflow.com/a/41717310/280393 explains how to create a list of immutable objects with references among them; However, the solution provided requires the objects to be known in advance.
How to achieve this when the objects are created on demand?
case class PersonA(id: Int, name: String, friends: Set[Int])
val john = PersonA(0, "john", Set(1,2))
val maria = PersonA(1, "maria", Set(0))
val georges = PersonA(2, "georges", Set(1))
val peopleA = Set(john, maria, georges)
case class PersonB(id: Int, name: String, friends: Set[PersonB])
// case class PersonB(id: Int, name: String, friends: () => Set[PersonB])
def convert(peopleA: Set[PersonA]): Set[PersonB] = ???
val peopleB = convert(peopleA)
println(peopleB)
println(peopleB.toList.map(_.friends.size))
peopleB.toList.map {
case PersonB(id, name, friends) => friends.size
}.foreach(println)
So, without modifying the implementation of case class PersonA and val peopleA, how to implement convert?
assuming that two PersonB instances are equal iff their id is equal,
one solution would be like this:
class PersonB(val id: Int, val name: String) {
var friends0: Set[PersonB] = _
def setFriends(friends: Set[PersonB]) {
require(friends0 == null)
friends0 = friends
}
def friends: Set[PersonB] = {
require(friends0 != null)
friends0
}
override def equals(that: Any): Boolean = that match {
case t: PersonB => t.id == id
case _ => false
}
override def hashCode(): Int = id.hashCode
override def toString = s"PersonB($id, $name, List(${friends.map(_.id).mkString(", ")}))"
}
object PersonB {
def apply(id: Int, name: String) = new PersonB(id, name)
def apply(id: Int, name: String, friends: Set[PersonB]): PersonB = {
val p = new PersonB(id, name)
p.setFriends(friends)
p
}
def unapply(p: PersonB): Option[(Int, String, Set[PersonB])] =
Some((p.id, p.name, p.friends))
}
def convert(peopleA: Set[PersonA]): Set[PersonB] = {
val peopleB = peopleA.map(p => new PersonB(p.id, p.name))
val peopleBMap = peopleB.map(p => (p.id, p)).toMap
peopleA.foreach(p =>
peopleBMap(p.id).setFriends(p.friends.map(peopleBMap))
)
peopleB
}
Is there a simpler way?
Udate Solution based on #sjrd answer:
class PersonB(val id: Int, val name: String, friends0: => Set[PersonB]) {
lazy val friends: Set[PersonB] = friends0
override def equals(that: Any): Boolean = that match {
case t: PersonB => t.id == id
case _ => false
}
override def hashCode(): Int = id.hashCode
override def toString = s"PersonB($id, $name, List(${friends.map(_.id).mkString(", ")}))"
}
object PersonB {
def apply(id: Int, name: String, friends: => Set[PersonB]): PersonB =
new PersonB(id, name, friends)
def unapply(p: PersonB): Option[(Int, String, Set[PersonB])] =
Some((p.id, p.name, p.friends))
}
def convert(peopleA: Set[PersonA]): Set[PersonB] = {
lazy val peopleB: Map[Int, PersonB] =
(for (PersonA(id, name, friendsIDs) <- peopleA)
yield (id, PersonB(id, name, friendsIDs.map(peopleB)))).toMap
peopleB.values.toSet
}
If you want to create immutable data you need to have a DAG / directed acyclic graph for creation order of you objects.
I do not think that you can do this since just the first 2 records have a cycle:
val john = PersonB(0, "john", Set(maria, ...))
val maria = PersonB(1, "maria", Set(john))
john is dependent on maria and maria on john.
So john is has to be created after maria and maria be created after john.
So you might have to compromise with your immutable data structures and not use case class but maybe an assign once instead.
Using this definition of PersonB:
case class PersonB(id: Int, name: String, friends: () => Set[PersonB])
you can write:
def convert(peopleA: Set[PersonA]): Set[PersonB] = {
lazy val peopleB: Map[Int, PersonB] =
(for (PersonA(id, name, friendsIDs) <- peopleA)
yield PersonB(id, name, () => friendsIDs.map(peopleB)).toMap
peopleB.values.toSet
}
Not tested, but something close to this should work.

Order class by field

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)

How do I make a parameter default to the value of another parameter in Scala?

Why can't I do the following:
def multiply(a: Int, b: Int = a) = a * b
or
case class Point(x: Int, y: Int = x)
Is there another way to achieve the same? The reason I need this is because sometimes, the arguments having different values is more the exception than the rule. Take for example:
case class User(name: String, age: String, description: String, displayName: String = name, online: Boolean = false)
In 90% of the cases, display name and name should be the same, but in a few edge cases, it should not. Having one parameter default to the value of another would be very useuful. Is there a way to do this? And if not, why?
Yes. Parameters in a parameter list can refer to argument values in previous parameter lists, similar to how type inference can refer to types in previous parameter lists:
def multiply(a: Int)(b: Int = a) = a * b
case class Point(x: Int)(y: Int = x)
case class User(name: String, age: String, description: String)(displayName: String = name, online: Boolean = false)
should work.
One way can be to define case class as
case class User(name: String, age: String, description: String, displayName: String, online: Boolean = false) {
def this(name: String, age: String, description: String, online: Boolean = true) =
this(name, age, description, name, online)
}
Then you can create case class as
val user = new User("User name", "5", "description")
//> user : User = User(User name,5,description,User name,true)
user.displayName
//> res0: String = User name
val userWithDisplayName = new User("User name", "5", "description", "displayName")
//> userWithDisplayName : User = User(User name,5,description,displayName,false)
You can also override apply method in the companion object. That way you will not have to write new before creating the object
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class User(name: String, age: String, description: String, displayName: String, online: Boolean = false)
object User {
def apply(name: String, age: String, description: String, online: Boolean) =
new User(name, age, description, name, online)
}
// Exiting paste mode, now interpreting.
defined class User
defined module User
scala> User("User name", "5", "description", "displayName")
res0: User = User(User name,5,description,displayName,false)
scala> User("User name", "5", "description", true)
res1: User = User(User name,5,description,User name,true)

Scala object cloning (copying) without value re-valuation

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")