Initialize val property on Kotlin Data Class via Secondary constructor - kotlin

class Animal {
val name: String
constructor(name: String){
this.name = name // initialized via constructor
}
}
For the above class in Kotlin I am able to initialize a val property via secondary constructor but the same is not allowed for Data classes
data class User(val name: String, val postalCode: Int) {
val email: String
constructor( email: String): this("", 1){
this.email = email // error: value can not be reassigned
}
}
What I can't understand is, where is the email property is initialized already as I haven't declared any initializes?

If your class has a primary constructor, you have to initialize all of its properties "in the primary constructor" - either by directly initializing them at their declaration:
val email = "foo#bar.com"
Or in an initializer block:
val email: String
init {
email = "foo#bar.com"
}
The compiler forces you to forward all secondary constructor calls to the to the primary constructor, and since the primary constructor already has to initialize all properties inside the class (otherwise calling it would construct a partially initialized instance, like in your code example), it wouldn't make sense to also initialize them in the body of the secondary constructor, especially for a val which cannot be reassigned.

Related

what is default value type of kotlin class constructor parameter?

class Greeter(name: String) {
fun greet() {
println("Hello, $name")
}
}
fun main(args: Array<String>) {
Greeter(args[0]).greet()
}
for above program I got this error
Unresolved reference: name
but when I add var or val
class Greeter(var name: String) {
or
class Greeter(val name: String) {
then program works fine, so why I need to add var or val to name, what is default type for constructor parameter val or var and why program gives me error when I not mention var or val
To use your value in the constructor like class Greeter(name: String), you can use init{}
class Greeter(name: String) {
var string:name = ""
init{
this.name = name
}
fun greet() {
println("Hello, $name")
}
}
or If you use val or var in the constructor it is more like class level variable and can be accessed anywhere inside the class
class Greeter(var name:String){
fun greet() {
println("Hello, $name")
}
}
The variable name can be used directly in the class then.
We can also give default values for the variables in both cases.
Adding val or var makes the parameter a property and can be accessed in the whole class.
Without this, it is only accessible inside init{}
The question is not making any sense, But the problem you are facing does make sense. In your case, the approach you are using is,
Wrong-Way:
// here name is just a dependency/value which will be used by the Greeter
// but since it is not assigned to any class members,
// it will not be accessible for member methods
class Greeter(name: String) {
fun greet(){} // can not access the 'name' value
}
Right-Way:
// here name is passed as a parameter but it is also made a class member
// with the same name, this class member will immutable as it is declared as 'val'
class Greeter(val name: String) {
fun greet(){} // can access the 'name' value
}
You can also replace val with var to make the name a mutable class member.

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

Kotlin, jackson: cannot annotate #JsonCreator in primary constructor

I want to annotate with #JsonCreator using a primary constructor, something like this:
// error
#JsonCreator class User(
#JsonProperty("username") var username: String,
#JsonProperty("password") var password: String
) {
// ...
}
But the #JsonCreator annotation gives an error "This annotation is not applicable to target 'class'".
Using a secondary constructor works, but is it the only (or best) way?:
// works, but is there a better way?
class User #JsonCreator constructor(
#JsonProperty("username") var username: String,
#JsonProperty("password") var password: String
) {
// ...
}
What you describe here:
class User #JsonCreator constructor(
#JsonProperty("username") var username: String,
#JsonProperty("password") var password: String
) {
// ...
}
is actually explicitly specifying the primary constructor. You can differentiate the primary from the secondary by looking at the class declaration:
class User constructor(/** **/) { // <-- primary
constructor(/** ... **/) { // <-- secondary
}
}
if the constructor is part of the class header it is a primary constructor, if it is part of the class declaration (it is after the {) it is a secondary one.

Kotlin delegate all fields by class property

I have a wrapper class Manager that has a property managerInfo of type UserInfo that I can’t modify. The wrapper class just add business rules around the info object. In code I need to access properties of the managerInfo and write each time manager.managerInfo.username is a little verbose.
I know that I can delegate property to this info object like that:
class Manager {
...
val username by UserInfo
...
}
And then I can simply do manager.username. But the problem is that the info class has about 15 properties and do this manually will be messy.
Is there is way to delegate all properties by means of Kotlin lang or by some library?
You can do implementation by delegation, which looks like this:
interface UserCommon {
val username: String
val email: String
}
data class UserInfo(
override var username: String,
override var email: String
) : UserCommon
class Transaction(
userInfo: UserInfo
) : UserCommon by userInfo
After that class Transaction will have all properties that UserCommon does, but implementation is delegated to userInfo which is passed to constructor.
The other way is to convert UserInfo to MutableMap and delegate property invocation to that map:
class Transaction(
userInfoMap: HashMap<String, Any>
) {
var username: String by userInfoMap
var email: String by userInfoMap
}

Data class constructor with two different constructor in Kotlin

I am new to Kotlin. I want to write a class which holds data. I want two constructor. What i want is something like this
class InstituteSearchDetails (var centerId: String) {
lateinit var centerId: String;
lateinit var instituteName: String;
lateinit var city: String;
init {
this.centerId=centerId
}
constructor( instituteName: String, city: String)
{
this.instituteName=instituteName;
this.city=city;
}
}
But on Secondary constructor line it says primary constructor call is required. I know some delegation is required which call primary constructor form there. I cant call primary constructor from here. I am sorry if i am doing some silly mistake. I am new to this thing
From the doc:
If the class has a primary constructor, each secondary constructor
needs to delegate to the primary constructor, either directly or
indirectly through another secondary constructor(s). Delegation to
another constructor of the same class is done using the this keyword:
Example:
class Person(val name: String) {
constructor(name: String, parent: Person) : this(name) {
parent.children.add(this)
}
}
Your code:
constructor( instituteName: String, city: String) : this("centerId"){
this.instituteName=instituteName;
this.city=city;
}
But it doesn't look like you have the centerId value in the secondary constructor.
You can have two secondary constructors:
class InstituteSearchDetails {
lateinit var centerId: String;
lateinit var instituteName: String;
lateinit var city: String;
constructor(centerId: String) {
this.centerId = centerId
}
constructor( instituteName: String, city: String)
{
this.instituteName=instituteName;
this.city=city;
}
}
But be aware that, for instance, centerId wouldn't have been initialized if you use the second constructor and you will get an exception (UninitializedPropertyAccessException) if you try to access the centerId in that case.
Edit:
This is not possible in data class because data class requires a primary constructor with at least one val or var. If you have the primary constructor, then your secondary constructor should delegate to the primary constructor as well. Perhaps you can have all properties in a single primary constructor of a data class but with nullable properties. Or see Sealed class.
sealed class InstituteSearchDetails {
data class InstituteWithCenterId(val centerId: String): InstituteSearchDetails()
data class InstituteWithNameAndCity(val name: String, val city: String): InstituteSearchDetails()
}
fun handleInstitute(instituteSearchDetails: InstituteSearchDetails) {
when (instituteSearchDetails) {
is InstituteSearchDetails.InstituteWithCenterId -> println(instituteSearchDetails.centerId)
is InstituteSearchDetails.InstituteWithNameAndCity -> println(instituteSearchDetails.name)
}
}