Secondary constructor in Kotlin - kotlin

I have 2 secondary constructors for a data class.
data class node(var type: String):parentNode(){
constructor(type: String, value: aNode) : this(type)
constructor(type: String, value: bNode) : this(type)
}
I want to return a value from a function which is node(type:String, value:aNode).
fun getNode(): node{
val aNode = getAnode
val type = "Bank"
val return_val = node(type,aNode)
return (return_val)}
a = getNode()
Now 'a' has only the 'type' but not 'aNode'.
Any idea on what am i missing here?

This is because value is not a property of node class. It is just a constructor argument. You need to put it as a property first and then initialize it from the constructor.
data class node(var type: String): parentNode() {
var value: parentNode? = null // Assuming aNode and bNode inherit from parentNode
constructor(type: String, value: aNode) : this(type) {
this.value = value
}
constructor(type: String, value: bNode) : this(type) {
this.value = value
}
}
Now you will be able to access this value using a.value. If the node class is instantiated using the primary constructor, a.value will be null.
Also, you might want to add private set to this value property so that it cannot be modified from outside. You can do the same with the type property (make it a val). Most of the times you would want to use val properties in a data class instead of vars.
(And it is recommended to follow Kotlin's naming conventions while creating variables, classes, functions, etc.)
Edit: As #gidds suggested, you can also include the value property in the primary constructor with a default value null and get rid of those secondary constructors.
data class node(val type: String, val value: parentNode? = null)

Related

How to establish getter & setter for secondary constructor in data class for kotlin?

I need a data class with two different constructors as shown. But how do I do getter & setter for the secondary constructor of data class in Kotlin? I tried multiple changes, not able to figure it out. In the below snippet, I am not getting the right import for get() and set()
data class user(var phone: String) {
constructor(phone: String, name : String) : this(phone) {
var name: String = name
get()= field
set(value) {
field = value
}
}
}
It appears you want two constructors, one which only requires a "phone" argument and another which requires both a "phone" and "name" argument. Overall, your data class will have two properties regardless of which constructor is used: phone and name. You could accomplish this with the following:
data class User(var phone: String) {
var name: String = ""
constructor(phone: String, name: String) : this(phone) {
this.name = name
}
}
However, as this is Kotlin, you should prefer default parameter values over overloaded functions/secondary constructors:
data class User(var phone: String, var name: String = "")

Defining a mutable property with a custom setter

The following class appears in "Kotlin in Action"
class Person(val name: String, age: Int) {
var age: Int = age
set(newValue) {
val oldValue = field
field = newValue
println("age changed from $oldValue to $newValue")
}
}
My understanding is that two things are happening in the constructor body
The age property is redefined as a mutable property
A custom setter is declared for age
Is there a simpler syntax that can achieve the same thing? Specifically is there a way to define a custom setter for a property that is defined as mutable in the property list?
class Person(val name: String, var age: Int) {
// how do I define a custom setter for age?
}
UPD: as #PaulHicks noted, in the first example, age is not a property, it's just a constructor parameter with the same name, and the property age is declared in the body and then just initialized from the constructor parameter. You can tell that by the keyword var or val missing from age: Int.
A property declared in a primary constructor is always a property with a backing field that is initialized from the constructor parameter and can't have custom accessors.
You can, however, make it a private mutable property and provide access to it via a property with a different name with just custom accessors and no backing field as follows:
class Person(val name: String, private var myAge: Int) {
var age: Int
get() = myAge
set(value) {
val oldValue = myAge
myAge = value
println("age changed from $oldValue to $value")
}
}
Note that the setter now references myAge and not field. This is called a backing property and is useful, for example, when you want a property to have a different public type than its privately stored value.

Same property name and primary constructor parameter name in Kotlin

I'm little confused how kotlin is managing the property name and the primary constructor parameter name. If I wrote the same property name and the parameter name then kotlin compiler gives an error.
class Student(name : String, roll : Int){
val name: String
init {
name = "Asif"
}
}
It gives this error.
> Error:(9, 5) Kotlin: Property must be initialized or be abstract
> Error:(12, 9) Kotlin: Val cannot be reassigned
But when I change the name of the property val name : String or the changing the name of the parameter of the primary constructor name : String then the code will work and compile.
This will work or compile fine.
class Student(pName : String, roll : Int){
val name: String
init {
name = "Asif"
}
}
What is the reason behind this? Why we can't have the same primary constructor's parameter name and the property name?
Primary constructor parameters are available in property initializers and initializer blocks (this is what makes the primary constructor special).
In your init block, name refers to the constructor parameter, which as all other function parameters, cannot be reassigned. This is the second error. The first one is for the same reason, now your property isn't initialized anywhere.
If you want to initialize your property, you can still refer to it as this.name:
class Student(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
Init block provides parameters from the default constructor. To assign name field of your object, you have to explicitly use this.name:
class Student(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
But more kotlin-way is to declare field with default value directly in the constructor:
class Student(val name : String = "DefaultName", roll : Int){ }
The answer was provided by others but to clarify look at these versions:
(a)
class Student1(name : String, roll : Int){
val name: String
init {
this.name = "Asif"
}
}
(b)
class Student2(name : String, roll : Int){
val name: String = "Asif"
}
(c)
class Student3(val name : String = "DefaultName", roll : Int)
Classes Student1 and Student2 are totally equivalent but Student3 is not: If you run the below code:
val s = Student1("Nick", 2)
println(s.name)
val s2 = Student2("Nick", 2)
println(s2.name)
val s3 = Student3("Nick", 2)
println(s3.name)
you will see:
Asif
Asif
Nick
Kotlin provides concise and easy way like below:
class Student(var name : String, roll : Int){
init {
name = "Asif"
}
}
Copied: In fact, for declaring properties and initializing them from the primary constructor, Kotlin has a concise syntax. Refer here

Ignoring certain properties when generating equals(), hashCode(), etc

Let's say I have a data class that has three properties:
data class Product(
val id: Int,
val name: String,
val manufacturer: String)
If I understand correctly, Kotlin will generate equals() and hashCode() using all the three properties, which will be like:
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other == null || javaClass != other.javaClass) return false
val that = other as Product?
return id == that.id &&
name == that!!.name &&
manufacturer == that.manufacturer
}
override fun hashCode(): Int {
return Objects.hash(id, name, manufacturer)
}
So what if I don't want id to be used in equals() and hashCode()? Is there a way to tell Kotlin to ignore certain properties when generating these functions? How about toString() and compareTo()?
For data classes, these functions are generated using all the properties that are declared in the primary constructor. From the official documentation:
The compiler automatically derives the following members from all
properties declared in the primary constructor:
equals()/hashCode() pair,
toString() of the form "User(name=John, age=42)",
componentN() functions corresponding to the properties in
their order of declaration,
copy() function (see below).
If you want a property not to be taken into account for their implementation, you'll have to either move it out of the primary constructor, or implement these functions yourself.
More discussion about a similar issue here.
A solution that has worked well for me is to separate your metadata from your data. e.g.:
data class Entity<out T>(val id: Int, val data: T)
data class Person(val name: String, val manufacturer: String)
Usage:
val e1 = Entity(1, Person("Mickey", "Disney"))
val e2 = Entity(2, Person("Mickey", "Disney"))
val e3 = Entity(3, Person("Donald", "Disney"))
e1 == e2
// false
e1.data == e2.data
// true
e2.data == e3.data
// false
From the documentation:
Note that the compiler only uses the properties defined inside the primary constructor for the automatically generated functions. To exclude a property from the generated implementations, declare it inside the class body.
Your example has to look like this:
data class Product(
val name: String,
val manufacturer: String) {
val id: Int
}
For more information take a look at: https://kotlinlang.org/docs/reference/data-classes.html#properties-declared-in-the-class-body

Override interface property with constructor parameter with different name

I have this code:
class AnyUsernamePersistentNodePath(override val value: String) : AnyPersistenceNodePath {
override val key = "username"
}
and
interface AnyPersistenceNodePath {
val key: String
val value: String
}
So far, so good. Now I want parameter value in the constructor to be named username, instead of value. But, obviously, keep overriding interface's property value. Is that possible in Kotlin?
I know that I can do:
class AnyUsernamePersistentNodePath(val username: String) : AnyPersistenceNodePath {
override val key = "username"
override val value = username
}
but I'd like to avoid it.
You can do what you want simply by removing val from the constructor parameter, so that it is a parameter and not a member. Your final class would be:
class AnyUsernamePersistentNodePath(username: String) : AnyPersistenceNodePath {
override val key = "username"
override val value = username
}
You cannot otherwise change the name of something that you are truly overriding. But you can pass in the value to be assigned to a member later during construction, as my slightly modified version of your code shows.