Query related with kotlin class - kotlin

class student1(firstName : String, lastName : String){
var id : Int = -1
val firstName = firstName
val lastName = lastName
init {
println("initialized")
}
constructor(firstName : String, lastName : String, extraParam : Int) : this(firstName, lastName){
this.id = extraParam
}
fun callme(){
print(firstName + lastName)
}
}
class student2(firstName : String, lastName : String){
val firstName = firstName
val lastName = lastName
fun callme() {
print(firstName + lastName)
}
}
fun main() {
val p1 = student1("shubham", "sharma")
println(p1.firstName)
println(p1.lastName)
println(p1.callme())
val p2 = student1("shubham", "sharma")
println(p2.firstName)
println(p2.lastName)
println(p2.callme())
}
here in both the class, the output is the same with the same parameter then why we need to use the secondary constructor? What is the main difference between these two class please let me know with one example. will be appreciated!

the first one has two constructors that get an additional variable but does not use it, that's why you don't see the difference. the student1 class has an optional id which is -1 by default. if you don't use it somewhere else you must remove it. actually, we don't create classes like this in kotlin you can move the var, val keywords in the constructor:
class Student1(val firstName : String, val lastName : String) {
var id = -1
init {
println("initialized")
}
constructor(firstName : String, lastName : String, id: Int = -1) : this(firstName, lastName) {
this.id = id
}
fun callme() {
print(firstName + lastName)
}
}
you can even make this shorter with default arguments and remove the secondary constructor and make id a val (if you don't want to change it):
class Student1(val firstName : String, val lastName : String, val id: Int = -1) {
init { println("initialized") }
fun callme() { print(firstName + lastName) }
}

There is no difference because you use only student1 !
val p2 = student1("shubham", "sharma")
instead of
val p2 = student2("shubham", "sharma")
To see a difference, you have to make it visible :
class student1 ...
fun callme(){
print(firstName + lastName + id)
}
Then use student1 with the secondary constructor :
val p3 = student1("shubham", "sharma", 2021)
println(p3.firstName)
println(p3.lastName)
println(p3.callme())
will output
shubhamsharma2021kotlin.Unit

Related

Why can't you change a property value permanently in kotlin?

Here's the code:
class Person(var firstName: String, var lastName: String) {
var fullName: String = firstName + lastName
fun fullName() = firstName + lastName
override fun toString(): String {
return fullName()
}
}
fun main(){
val test = Person("test", "fortest1")
test.lastName = "fortest2"
println(test.fullName)
}
The result will only be testfortest1.
It looks like you are working with a copy of test once test is created.
This is because fullName is not observing any changes to firstName or lastName. It is initialized when Person is created and stays the same unless explicitly modified.
One easy fix for this is to provide a custom getter like this:
val fullName get() = firstName + lastName
Now it will work as you expect because everytime you read fullName that expression will be evaluated and the result will be returned.
(Also, prefer using vals over vars in data class fields)

KOTLIN: Can I Call Multiple Class Methods Outside Apply block and Difference in Calling Methods Inside and Outside of Apply Block?

package com.example.learning
fun main(args: Array<String>) {
var person = Person("Ramu", 51, 50000F, 7.5F)
person.apply{
name = "Sam"
id = 1
details()
}.details()
}
Q1)What is the difference in calling details or any class method inside and outside the apply block?
Q2)Here in the apply block can I call multiple class methods at once like calling Salary and Details at the same time outside the apply block with .details()&.salary()?
class Person(var name: String, var id: Int, var sal: Float, var salRaise: Float){
fun details(){
println("The name of the person is $name with id $id and salary if $sal")
}
fun salary(): Float{
sal *= salRaise
return sal
}
}
apply is just a convenience method so you don't have to write person every time.
This:
person.apply{
name = "Sam"
id = 1
details()
}
is the same as doing:
person.name = "Sam"
person.id = 1
person.details()
I'm not sure what you mean by your second question. You ask how to call those 2 methods outside a apply? Just do
person.details()
person.salary()
There's another benefit to using apply. If person could be null you could do person?.apply to only change those fields in the case the person is not null. So this for example:
fun changePerson(person : Person?) {
person?.apply{
name = "Sam"
id = 1
details()
}
}
is the same as
fun changePerson(person : Person?) {
if (person != null) {
person.name = "Sam"
person.id = 1
person.details()
}
}
EDIT:
If you want to be able to chain the salary() after a details() you could do it by making this change:
class Person(var name: String, var id: Int, var sal: Float, var salRaise: Float){
fun details() : Person{
println("The name of the person is $name with id $id and salary if $sal")
return this
}
fun salary(): Float{
sal *= salRaise
return sal
}
}
then you could do this for example:
person.apply{
name = "Sam"
id = 1
}.details().salary()

Primary constructor call expected

this is example of inheritance in Kotlin.
open class Person() {
var _name : String = ""
var _age : Int = 0
constructor(name: String, age : Int) : this(){
_name = name
_age = age
}
}
class Student() : Person() {
var _university : String = ""
constructor (name : String, age: Int, university: String) : super(name, age){
_university = university
}
}
fun main() {
var person = Person("a", 10)
var student = Student("b", 18, "MIT")
println("${person._name} : ${person._age}")
println("${student._university}")
}
How can I resolve error "Primary constructor call expected" in this case?
If a class has a primary constructor, you must delegate the secondary constructors to the primary. Which is the case for Student class which is not doing so.
First you should remove the boilerplate of Person class following Kotlin's idioms (using optional parameters rather than constructor overloading).
// This one's completely optional to do, skip if you don't want to follow idioms
open class Person(
var name : String = ""
var age : Int = 0
)
And then Student class should have two separate constructors, because if you take a primary constructor then other constructors must delegate to it:
class Student: Person {
var university: String = ""
constructor() : super()
constructor (name: String, age: Int, university: String) : super(name, age) {
this.university = university
}
}

How to find the updated fields between a payload and an entity fetched from DB and create an object having fields with updated values and rest Null

Given an update request for a record in DB, I have to find a difference between the payload and existing data in DB then create a new Object which has updated fields with Payload values and rest as Null.
I have created a function which gives me a list of field names which were updated, But I'm unable to create a new object which has values for only these updated fields.The problem is that the function uses "field: Field in cpayload.javaClass.declaredFields" which is kind of generic so I'm unable to set these fields.
fun findupdatedFieldsList(cpayload: Customer, cEntity: Customer): List<String> {
// var customerToPublish = Customer()
val updatedFieldsList: MutableList<String>
updatedFieldsList = ArrayList()
for (field: Field in cpayload.javaClass.declaredFields) {
field.isAccessible = true
val value1 = field.get(cpayload).toString()
val value2 = field.get(cEntity).toString()
!Objects.equals(value1, value2).apply {
if (this) {
// customerToPublish.birthDate=field.get(cpayload).toString()
updatedFieldsList.add(field.name)
}
}
}
return updatedFieldsList
}
#Entity
#Table
data class Customer(
#Id
val partyKey: UUID,
var preferredName: String?,
var givenName: String?,
var lastName: String?,
var middleName: String?,
var emailAddress: String,
var mobileNumber: String,
val birthDate: String?,
val loginOnRegister: Boolean,
var gender: Gender?,
var placeOfBirth: String?,
var createdDate: LocalDateTime = LocalDateTime.now(),
var updatedDate: LocalDateTime = LocalDateTime.now()
)
Desired Output
val customer = Customer(
preferredName = Updated name,
partyKey = partyKey.value,
givenName = Updated name,
lastName = null,
middleName = null,
emailAddress = Updated email,
mobileNumber = null,
birthDate = null,
gender = null,
placeOfBirth = null
)
I was able to construct a solution using Kotlin's reflect. It is generic and can be applied to any Kotlin class that have primary constructor. Unfortunately it won't work with Java classes
You would need to add kotlin-reflect package to your build tool config, e.g. for Gradle:
implementation 'org.jetbrains.kotlin:kotlin-reflect:XXXXXX'
First we will build a function to extract updated properties. Please take a note that we also need to extract properties that are mandatory (non-nullable and without default). We add them to a map of propertyName -> propertyValue:
fun Map<String?, KParameter>.isOptional(name: String) = this[name]?.isOptional ?: false
fun <T : Any> findUpdatedProperties(payload: T, entity: T): Map<String, Any?> {
val ctorParams = payload::class.primaryConstructor!!.parameters.associateBy { it.name }
return payload::class.memberProperties.map { property ->
val payloadValue = property.call(payload)
val entityValue = property.call(entity)
if (!Objects.equals(payloadValue, entityValue) || (!ctorParams.isOptional(property.name))) {
property.name to payloadValue
} else {
null
}
}
.filterNotNull()
.toMap()
}
Then we call this function and construct a new instance of provided class:
fun <T : Any> constructCustomerDiff(clazz: KClass<T>, payload: T, entity: T): T {
val ctor = clazz.primaryConstructor!!
val params = ctor.parameters
val updatedProperties = findUpdatedProperties(payload, entity)
val values = params.map { it to updatedProperties[it.name] }.toMap()
return ctor.callBy(values)
}
Take a note that missing primary constructor will throw NullPointerException because of use of !!.
We could call this funcion as constructCustomerDiff(Customer::class, payload, entity), but we can do better with reified types:
inline fun <reified T : Any> constructCustomerDiff(payload: T, entity: T): T {
return constructCustomerDiff(T::class, payload, entity)
}
Now we can use this function in convenient Kotlin style:
val id = UUID.randomUUID()
val payload = Customer(
partyKey = id,
preferredName = "newName",
givenName = "givenName"
)
val entity = Customer(
partyKey = id,
preferredName = "oldName",
givenName = "givenName" // this is the same as in payload
)
val x = constructCustomerDiff(payload, entity)
assert(x.partyKey == id && x.givenName == null || x.preferredName == "newName")

type-safe builder example kotlin

I want to have following person object in Kotlin :
var p = person {
age = 22
gender = "male"
name {
first = "Ali"
last = "Rezaei"
}
}
I have following code to build it :
data class Person(var age: Int? = null, var gender: String? = null
, var name : Name? = null) {
}
fun name(init: Name.() -> Unit): Name {
val n = Name()
n.init()
return n
}
data class Name(var first: String? = null, var last : String? = null)
fun person(init: Person.() -> Unit): Person {
val p = Person()
p.init()
return p
}
But when I print it, the result is following :
Person(age=22, gender="male", name=null)
What is wrong with my code?
You could make name an extension function on Person that assigns the Name to the Person instead of returning it:
fun Person.name(init: Name.() -> Unit) {
val n = Name()
n.init()
this.name = n
}
You could even consider a more concise syntax for the same, like this:
fun Person.name(init: Name.() -> Unit) {
this.name = Name().apply(init)
}
Shameless plug for my repository discussing DSL design and containing examples.
You need to assign to name. This ended up working for me...
var p = person {
age = 22
gender = "male"
name = name {
first = "Ali"
last = "Rezaei"
}
}