I am learning the scala and I have try with the following code.
object Demo7 {
def main(args: Array[String]): Unit = {
class Person(val fullName: String) {
println(s"This is the primary constructor. Name is ${fullName}")
val initial = fullName.substring(0, 1) // Computed during initialization
//def this(firstName: String, lastName: String) = this(s"$firstName $lastName")
}
new Person("Tek Tuk")
new Person("Tek Tuk").fullName
}
}
then I run I get the same returned result as each call.
for my understanding this line
new Person("Tek Tuk").fullName
Shouldn't compile, anyone can explain me why this line get compile and return the same result as the first line?
Thank you.
If you're asking why you're allowed to access the fullName field of your Person class, that's because you've declared it as a val in the parameter list.
This is the same as declaring it a public final field in Java. If you want it to be private, just remove the val part, i.e.
class Person(fullName: String) {
(...)
}
As for why both calls "return" the same thing - they don't.
new Person("Tek Tuk") returns an instance of Person.
new Person("Tek Tuk").fullName returns "Tek Tuk" - a String. You created another instance of Person with the same fullName and you called fullName on it.
Both, however, print "This is the primary constructor. Name is Tek Tuk", because you called the same constructor in both cases and you have a println that prints this in the constructor.
Related
I would like to declare some auxiliary values inside a case class constructor, but it seems not to be correct Scala.
In short, the following piece of code is correct:
case class Something(
text1: String,
text2: String
) {
def this(datetime: LocalDateTime) {
this(
s"date: ${datetime.toLocalDate.toString()}",
s"time: ${datetime.toLocalTime.toString()}"
)
}
}
and the following is not:
case class Something(
text1: String,
text2: String
) {
def this(datetime: LocalDateTime) {
val date = datetime.toLocalDate.toString()
val time = datetime.toLocalTime.toString()
this(
s"date: $date",
s"time: $time"
)
}
}
even though the latter would be more legible and easier to maintain. (Imagine using more complex operations than just calling two methods.) Why is that?
Is there another way to write a constructor like that or a way to work around this?
In Scala first call must be to primary constructor. After that you can have as much code as you want. Read this for explanation.
Similar rule applies to Java for this and super. Not exactly same though. Read this.
The reason why this and super must be first is, that one can set fields to various values before that actual this(x, y) is called. This means object is being constructed and different values can be visible to any thread that may have reference to the object while construction is in progress.
Thanks.
In your second case you are not allowed to define variables inside constructor before this(params) call, as computing inside constructors are discouraged in scala class or case class. One way you can fix it is pass inline constructor params.
test("test case class custom constructor") {
case class Something(text1: String,text2: String) {
def this(datetime: LocalDateTime) {
this(datetime.toLocalDate.toString(), datetime.toLocalTime.toString())
//you can do whatever you want after this(x, y) is invoked
val testVal = "apple"
println(testVal)
}
}
new Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
new Something(LocalDateTime.now()).text2 should not be empty
}
Another way (Encouraged way) is define case class and then define apply inside a companion object as below (for older version maybe 2.11.8, companion object had to be defined first and only case class which seems to be fixed now - https://issues.scala-lang.org/browse/SI-3772)
test("test class with companion apply method") {
case class Something(val text1: String, val text2: String) {}
object Something {
def apply(datetime: LocalDateTime): Something = {
val x = datetime.toLocalDate.toString()
val y = datetime.toLocalTime.toString()
new Something(x, y)
}
}
Something(LocalDateTime.now()).text1 shouldBe "2017-07-16"
Something(LocalDateTime.now()).text2 should not be empty
}
scastie code - https://scastie.scala-lang.org/prayagupd/yn2bJWHkQ6Gbli5Ll6I6CQ/1
Auxiliary constructors have a constraint that it should call a previous auxiliary constructor or primary constructor on the first line of its body. The second code does not follow that rule. Hence the error.
Why does subclass create a copy of field when inheriting using a primary constructor?
For example
object Question
{
class Animal(var name : String) {
def setName(name : String) {
this.name = name
}
}
class Dog(name : String) extends Animal(name) {
this.setName("dog: " + name)
override def toString: String = this.name // this actually returns "Billy"
}
def main(args: Array[String]) {
val dog = new Dog("Billy")
println(dog.toString) // output "Billy", why?
println(dog.name) // output "dog: Billy", why?
}
}
As you can see, dog.toString() returns "Billy" instead of "dog: Billy", does it mean this.name is different from inherited name ? If so, then why does dog.name return "dog: Billy" ?
If you run your compiler with the -Xlint option you should see the following.
warning: private[this] value name in class Dog shadows mutable name
inherited from class Animal. Changes to name will not be visible
within class Dog - you may want to give them distinct names.
If you rename the name argument to the Dog class, the warning goes away and the output is more consistent and perhaps closer to what's expected.
Here's your code debug result
.
So, what you can see is that there are really actually two instances of variable "name". One is from "Animal", and one from "Dog". Constructors work a little bit different in Scala - what you call constructor arguments are actually local variables that are initialized with constructor arguments.
I can't really provide any elegant solution to this, unfortunately.
Also, I might be wrong. Please correct me if I am.
Update
Try this question.
class Person(){
val name : String
def this(n : String) {
this()
this.name = n
}
}
compile time error : reassignment to val
i am a newbie to scala and so far i learned how to use primary constructor and case classes for initialization of data members. I am just wandering, if there is a way to initialize val data member inside this. Initialization of var data member works fine below :-
class Person(){
var name : String = _
def this(n : String) {
this()
this.name = n
}
}
You just can't assign to a val after initialization. In Scala the body of the class IS the constructor, you can see examples here.
In general, you just define all variables in the primary constructor itself as "class parameters": class Person(val name: String) if you need to receive the name for initialization or class Person() { val name = 'Joe' } if it is fixed.
This can be quite surprising coming from Java, as you are used to have constructors that produce the values and build the object directly. For such a case, the best solution is to use apply methods on a companion object:
class Person(val name: String)
object Person() {
def apply(db: SomeDBConnectionWrapper, id: Int) = new Person(db.fetchName(id))
}
That allows you to call Person(db, 3) to get a new person with custom initialization, but the constructor itself still receives all it needs to construct a new instance where all values are only assigned once.
I am trying to create a custom field type in Squeryl. This field represents an Isin code, so it is backed up by a string field. Following the example on the documentation, I have added a simple validation before creating a new isin (never mind what an Isin code is or how the validation procedure works):
trait Domain[A] { self: CustomType[A] =>
def validate(a: A): Unit
validate(value)
}
class Isin(v: String) extends StringField(v) with Domain[String] {
println("instantiating Isin")
override def validate(s: String) {
println("calling validate with " + s)
assert(checkIsin(s))
}
private def checkIsin(isin: String): Boolean = {
// never mind the exact procedure
}
}
I have added some println to find out what is going on. I use this field inside a model like
case class Asset(
val id: Long = 0,
val isin: Isin
) extends KeyedEntity[Long]
object Asset {
import Database.assets
def create(isinCode: String) {
inTransaction {
assets.insert(new Asset(isin = new Isin(isinCode)))
}
}
}
Now, when I call Asset.create("US0378331005") (a valid ISIN) I get an exception. In the stacktrace it turns out that this exception is due to a call to the init method on a null value, which is supposedly passed to checkIsin. Indeed, the println statements print
calling validate with US0378331005
Instantiating Isin
calling validate with
So it seems that the validate method is actually invoked twice, but the second time it gets a null value.
What is going on wrong?
There are several problems here. First off, you seem to be using Lift Record and, if that's the case, you are implementing your validation logic incorrectly. The correct way to validate a Record field is to override
def validations: List[ValidationFunction]
where ValidationFunction is a type alias
type ValidationFunction = ValueType => List[FieldError]
and in your case ValueType == String.
The next issue is your Domain trait. Because your call to validate is inlined into the class definition, it will be called when your field is constructed. Obviously there is no value set at that point, so that's why you are seeing the println that references an empty value. I imagine that the order you see them in has something to do with your application flow. You have an existing record that is validated, and after that a new Record is created which triggers the next 2 println statements.
I've got a Scala def that takes parameters from an HTTP POST and parses the data. I'm pulling a "job" object from the database (the query was successful as verified in the debugger, and parameters are just as they need to be) and I'm trying to update that job object with the new parameters. However, trying to assign values are proving useless since the job object retains all original values.
All database objects are from Squeryl. Code below:
Edit: added class below and Job object to help give context in this Play! app
object Job {
def updateFromParams(params:Params) = {
val job = Job.get( params.get("job_id").toLong ).get
val comments = params.get("comments")
val startTime = parseDateTime(params.get("start_time") + " " + params.get("date"))
val endTime = parseDateTime(params.get("end_time") + " " + params.get("date"))
val clientId = params.get("client_id").toLong
val client = Client.get(clientId).get
val name = params.get("job_name")
val startAddressType = params.get("start_address_type")
var startLocationId:Option[Long] = None
val (startAddress, startCity, startProvince) = startAddressType match {
case "client" => getClientAddress(clientId)
case "custom" => (params.get("start_custom_address"),
params.get("start_custom_city"),
params.get("start_custom_province"))
case id => {
startLocationId = Some(id.toLong)
getLocationAddress(startLocationId.get)
}
}
job.comments -> comments
job.startTime -> startTime
job.endTime -> endTime
job.clientId -> clientId
job.name -> name
job.startAddressType -> startAddressType
job.startAddress -> startAddress
job.startCity -> startCity
job.startProvince -> startProvince
Job.update(job)
}
}
I'm stumped because if I try job.name -> name nothing happens and if I try job.name = name then I get a Scala reassignment to val error. I get the same error when trying var name instead of val name.
It's obviously a syntax issue on my part, what's the proper way to handle this? Thanks!
More Info: if this helps, here's the Job class used in our Play! app:
class Job(
val id: Long,
#Column("name")
val name: String,
#Column("end_time")
val endTime: Timestamp,
#Column("start_time")
val startTime: Timestamp,
#Column("client_id")
val clientId: Long,
#Column("start_address_type")
var startAddressType:String,
#Column("start_address")
var startAddress: String,
/* LOTS MORE LIKE THIS */
) extends KeyedEntity[Long] {
}
job.name is an immutable property, so you cannot change its value with job.name = name. You can see in the definition of the Job class that name is declared with val, meaning its value is immutable and can never be changed. The only way to "change" the values of the job object is to actually create a totally new instance and discard the old one. This is standard practice when dealing with immutable objects.
Changing your local name from val to var won't matter, since you are only reading the value of that variable.
val are immutable, in fat the whole Job class is immutable (since all fields are).
What could be done is to create a case class JobW and a bit of pimping to allow the use of copy. That said:
class Job(val id:Long, val name:String) {}
case class JobW(override val id:Long, override val name:String) extends Job(id, name){
def ok:String = name + id
}
implicit def wrapJob(job:Job):JobW = JobW(job.id, job.name)
val job:Job = new Job(2L, "blah")
println(job.ok)
println(job.copy(name="Blob"))
What I've done, is to wrap a (spimplified for the exercise) Job into a case class wrapper, and define the implicit conversion.
Using this implicit conversion (what is called pimping), you'll have access to the ok method but also the copy one.
The copy method is an injected one on case classes, that takes as much arguments as the case class as fields and produces a new instance of the case class.
So you have now the ability to change only one value of you class, very simply I mean, and retrieve an new object (as functional programming arges for immutability).