Difference Scala Class Declaration - scala

I want to ask what's the difference between these two Class Declarations below.
class Person(name: String, age: Int)
or
class Person() {
var name: String = ""
var age: Int = 0
}

class Person(name: String, age: Int)
name and age are constructor parameters. As such they are:
required - in order to create a Person instance
immutable - the values cannot be changed
private - (by default) and cannot be accessed via a class instance
class Person() {
var name: String = ""
var age: Int = 0
}
name and age are class data members. They are:
not specified during construction - but are given the same default values for every Person instance
mutable - because they are var variables
public - (by default) and can be accessed and modified via a class instance
So, what's the difference? Almost everything.

The first declaration uses name and age as constructor arguments, but they do not become members of the class, that is you can't write person.name.
For a beginner, the second line can be considered a more complicated way to write class Person(var name: String = " ", var age: Int = 0). It actually doesn't translate to exactly the same thing: your version creates a class that has a single constructor argument and two public variables, while mine has a constructor with 2 arguments that have default values. Unless you have a good reason to declare it your way, it's usually better to write:
val person = new Person("Andrew", 11)
than
val person = new Person()
person.name = "Andrew"
person.age = 11
which is what your second version would force you to do.
Note that in Scala, you will mostly be using values (constants) and not variables, so you'd actually typically use class Person(val name: String = " ", val age: Int = 0). For simple data types like this, people typically use a case class:
case class Person(name: String, age: Int)
In this case, both argument are considered public and immutable. The only way to modify a person's name is to create another instance of Person with the new name:
val andrew = Person("Andrew", 11)
val will = andrew.copy(name="Will") # Will is also 11
Case classes will automatically give you proper equals and hashCode implementation, as well as the copy method I used above. You can easily find more infos about them on the internet.

Related

Why are not class parameters not being called on the Object in Scala?

In Scala how are not class parameters not truly the values being called on the object? The class parameters are part of primary constructor, so should it not be setting up the member variables of the objects, like Java? Why must we make separate fields that are the values being set on the objects? Rather than just accepting the fact, is there any good explanation?
If you want to call the parameters assigned in class's primary constructor, you will have to declare them as field.
class Man(name: String, age: Int) {
def show = "My name is " + name + ", age " + age
}
Here name and age is constructor parameter. They are only accessible in the class scope.
class Man(val name: String, age: Int) {
def show = "My name is " + name + ", age " + age
}
Notice name is now a variable. So now you can access name with Man class's instance.
val x = new Man("Jack",10)
x.name // Jack
But you can not access age because it is a parameter not field.
The class parameters are part of primary constructor, so should it not be setting up the member variables of the objects, like Java?
Even in Java constructor parameters do not automatically become class members
public class User {
public User(String name, Integer age) {
// do something
}
}
User user = new User("Picard", 75);
String name = user.name // error
so we have to do something like
public class User {
String name;
Integer age;
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
}
Why must we make separate fields that are the values being set on the objects?
Using case class we get that for free (along with few other things)
case class User(name: String, age: Int)
val user = User("Picard", 75)
val name = user.name // ok
In Scala how are not class parameters not truly the values being called on the object?
Indeed, in certain cases class parameters are implemented as fields with the scope set to private[this].
Here is an excerpt from the discussion in scala-lang website.
A parameter such as class Foo(x : Int) is turned into a field if it
is referenced in one or more methods
Such a field is as if it had been declared as private[this] val x: Int.
If the parameter isn't referenced in any method, it does not give
rise to a field.

What's the difference between handling fields throught "accessor" and "copy"

I don't understand the difference between handling a field through accessor and copy method in Kotlin. It is like this:
Accessor example :
class Person(val name: String,
var age: Int)
fun happyBirthday(person: Person) {
person.age++
}
Copy method example :
data class Person(val name: String,
var age: Int)
fun happyBirthday(person: Person) {
person.copy(age = person.age + 1)
}
person.age++
modifies your existing person instance by incrementing its age by 1.
person.copy(age = person.age + 1)
on the other hand returns a new instance of Person that has the same properties as person does, except for the age that you've specified. As your sample code is right now, you're not assigning this new Person instance to anything, you're just throwing it away.
This copy method is useful when your class is immutable (all of its properties are vals) - instead of modifying it, it gives you an easy way to create a new instance that has mostly the same properties as the original.

Trying to implement a UUID(random unique identifier) generated from a method

New to scala, what is the best way to implement this method. I am trying to get this value output from the method below and assign it to a case class. Bit rusty on the OOP practices.
/** A container for storing car table fields */
case class car(
UUID??
color:String,
model:String,
type:String,
)
Basicaly my question what is the best way to create an instance of the below rand value to the case class car above. Create another class and call it or implement with in the same scala class?
def rand = new Random()
def randomClockSeqAndNodeFields = {
var lsb: Long = 0
lsb |= 0x8000000000000000L // variant (2 bits)
lsb |= ( rand.synchronized { rand.nextLong } & 0x3FFFFFFFFFFFFFFFL)
lsb
}
I suggest, since UUID is a java supported type :
/** A container for storing car table fields */
case class car(
uuid: UUID = UUID.randomUUID(),
color:String,
model:String,
type:String,
)
One way to organize this code is to define a companion object:
object Car {
def rand = new Random()
def randomClockSeqAndNodeFields = { ... }
}
case class Car(
UUID: Long = Car.randomClockSeqAndNodeFields,
color: String,
model: String,
make: String
)
You can call your method inside the declaration of your case class, and that method will, by default, be called for every instance of the class. Note that I capitalized the class name to follow standard naming conventions, and I renamed type to make because type is a keyword.
To create a Car instance:
val myCar = Car(color="black", model="wrangler", make="jeep")
The creation of every Car instance, if you don't explicitly pass in a value for the UUID parameter, will call randomClockSeqAndNodeFields, generating a new UUID for that car. Alternatively, you could define Car as...
case class Car(
UUID: Long,
color: String,
model: String,
make: String
)
...in which case you'd have to explicitly call randomClockSeqAndNodeFields every time you create an instance:
val myCar = Car(Car.randomClockSeqAndNodeFields, "black", "wrangler", "jeep")

Scala - How to get constructore fields values in reflection?

I'm pretty new to scala and struggling a bit with reflection.
Given the below class:
class Person (name: String) {
.....
}
and the given instance:
val p = new Person ("MyName")
How can i retrieve the value "MyName" in reflection?
BTW - I tried with java reflection using getClass.getDeclaredFields(), but without luck...
Thanks!
name is not a member of the class in your example, it's just a function parameter of the constructor. The difference in scala in rather subtle, because the whole class is defined inside the constructor body, so, it is, effectively, a closure, and you can use the parameters everywhere, but still, there is a difference.
class Person(val name: String) or class Person(var name: String) declares a class with an instance member (either final or writable) name.
class Person(name: String) has no instance variables, name is just a parameter to the constructor.
Case classes are special, as they treat all constructor parameters as vals by default, this is "syntactic sugar": case class Person(name: String) really means `case class Person(val name: String).
I think you should add modifier var or val when defining constructor.
i.e,
class Person (var name: String) {
.....
}
and to retrieve value "MyName", you can do,
val p= new Person("MyName");
and Name is: p.name

How to define accessor method for default constructor parameter?

Trying to define an accessor method for default constructor parameter, i.e.:
class Person (age: Int) {
def age: Int = this.age
}
Which obviously results in a compiler error: ambiguous reference to overloaded definition, both method age in class Person of type => Int and value age in class Person of type Int match expected type Int
Is there a way in this context to distinguish between the member method name and auto-generated member value name?
Of course it's possible to change the name of either identifier, but is there a way in this scenario of actually specifying which identifier is referred to?
Just put "val" in front of constructor parameters that you want to expose as instance properties.
Use
class Person (val age: Int)
if you just want a getter or
class Person (var age: Int)
if you also want a setter.
The answers above are great wrt the uniform access principle. If you have or need Java style getters and setters you can also use the BeanProperty annotation.
class Person(#scala.reflect.BeanProperty var age: Int)
This will result in the following methods being created:
def getAge: Int = age
def setAge(age: Int) = this.age = age
If you instead use the BeanProperty for a val instead of a var, the setter won't be created, only the getter.
One other caveat, the setter method cannot be called from inside Scala. Instead, you should use the standard Scala convention of uniform access to set the value.
Just for completeness and to expand on the previous answers, there is also the technique covered here.
To summarize, I would always begin with an immutable value:
class Person (val age: Int)
Then, if you figure out you need to mutate the value (or you know it in advance), switch to:
class Person (var age: Int)
Then, if you need to validate or do some other computation on get or set, rename your variable and build accessors that mimic your original naming, no need to refactor the rest of the code:
class Person(var _age: Int)
{
def age =
{
println("age requested")
_age
}
def age_=(newAge: Int) =
{
assert(newAge > 0)
println(s"age changed from ${_age} to $newAge")
_age = newAge
}
}
Of course, you can simplify either setter or getter if you don't need operations there.
Kudos to all other answers, which are indeed correct and came much sooner.