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.
Related
I am new to Scala and I come from a Java background.
I would like to know the Scala equivalent of this Java code:
public class Person {
String name;
public Person(String name) {
this.name = name.toLowerCase();
}
}
So, when creating Person object, we directly put the lower case version
This is roughly what you want:
class Person private(val name: String)
object Person {
def apply(name: String): Person =
new Person(name.toLowerCase)
}
val p = Person("Tim") // == Person.apply("Tim")
p.name // tim
The private modifier means that you cannot create the class directly using new.
The object has the same name as the class and is the companion object for the class. It has full access to the class, including being able to construct instances using new.
The apply method is used when you "call" an object, and this modifies the parameters before calling the actual constructor.
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.
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
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.
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.