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

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()

Related

map a nested list to another nested list in Kotlin

I have a nested list of People : List<List<People.>>, where People has two attribute int age, String name.
I want to map it to a nested list of Student, Student also has two attribute int age, String name.
So the output is List<List<Student.>>.I have looked at examples of mapping a List to another, something like this:
fun List<People>.convert(): List<Student>{
return this.map {
Student(
age = this.age,
name = this.name
)
}
}
How to do it with a nested list? Thanks in advance.
map is one of Kotlin's collection transformation operations. That link explains how it works.
Let's fix your List<People>.convert() function first. Here's one way to write it:
data class Person(val name: String, val age: Int)
data class Student(val name: String, val age: Int)
fun List<Person>.convert(): List<Student> {
return this.map { person ->
Student(
age = person.age,
name = person.name,
)
}
}
Note that inside the mapping function, this does not refer to anything, which is why your original code doesn't compile.
Since the mapping function we're passing to map has only one parameter, we can skip declaring the parameter, and refer to the argument by the special name it instead, like this:
fun List<Person>.convert(): List<Student> {
return this.map { // it is Person
Student(
age = it.age,
name = it.name,
)
}
}
Then, to convert a List<List<Person>> to a List<List<Student>> we could write:
val listsOfPeople: List<List<Person>> = listOf(
listOf(Person("Alice", 27)),
listOf(Person("Bob", 23), Person("Clarissa", 44))
)
val listsOfStudents: List<List<Student>> = listsOfPeople.map { // it is List<Person>
it.convert()
}
Or, if you decide you don't need the convert function, you could write it like this:
val listsOfStudents: List<List<Student>> =
listsOfPeople.map { // it is List<Person>
it.map { // it is Person
Student(it.name, it.age)
}
}

Query related with kotlin class

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

can you join two tables and result with an obj (from first table) containing a list of obj(from the second table)

First of all my code:
Table 1:
object Company : Table() {
val name = varchar("pk_name", 250)
override val primaryKey = PrimaryKey(name, name = "pk_company_constraint")
}
Table 2&3:
object Sector : IntIdTable() {
val name = varchar("fk_name", 50).references(MainSector.name)
val alias = varchar("alias", 50).nullable()
val companyName = varchar("fk_company_name", 250).references(Company.name, onDelete = ReferenceOption.CASCADE)
}
object MainSector : Table() {
val name = varchar("pk_name", 50)
override val primaryKey = PrimaryKey(name, name = "pk_main_sector_constraint")
}
My Problem:
I need to parse the result into a DTO that looks like this:
data class CompanyDTO (
val companyName: String,
val sectorList: List<SectorDTO>
)
data class SectorDTO (
val mainSectorName: String,
val sectorAlias: String
)
I am able to get a Company with the first Sector from the database, but i have no idea how to get a list of them.
My try:
override fun retrieveCompanies(vararg names: String): List<CompanyDTO> {
var retlist: List<CompanyDTO> = emptyList()
if (names.isEmpty()){
retlist = transaction {
(Company innerJoin Sector)
.select{Company.name eq Sector.companyName}
.map { CompanyDTO(it[Company.name], listOf(
SectorDTO(it[Sector.name], it[Sector.alias]?: "")
)) }
}
} else {
//return specific
}
return retlist
}
If no arguments are given i want to return all companies from the database, if arguments are given i want to return only companies with given name.
I canĀ“t find anything about this topic in the official documentation, please send help
If Company could not have any Sector you need to use leftJoin and then your code could be like:
Company.leftJoin.Sector.selectAll().map {
val companyName = it[Company.name]
val sector = it.tryGet(Sector.name)?.let { name ->
SectorDTO(name, it[Sector.alias].orEmpty())
}
companyName to sector
}.groupBy({ it.first }, { it.second }).map { (companyName, sectors) ->
CompanyDTO(companyName, sectors.filterNotNull())
}

Re-use mapping code for immutable data class in Kotlin

Updated: added some clarifications from the comments
I would like to use the same 'mapping' code for the primary constructor and copy() method of an immutable data class. How can I do this without creating an empty object first, and then using copy() on it?
The issue with how it is now is that if I add a new attribute with default value to Employee and EmployeeForm it would be easy to only add it in one of the two mapping functions and forget about the other (toEmployeeNotReusable / copyEmployee).
These are the data classes I'd like to map between:
#Entity
data class Employee(
val firstName: String,
val lastName: String,
val jobType: Int,
#OneToMany(mappedBy = "employee", cascade = [CascadeType.ALL], fetch = FetchType.EAGER)
private val _absences: MutableSet<Absence> = mutableSetOf(),
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long = 0 // prevents #Joffrey's answer from working
) {
init {
_absences.forEach { it.employee = this }
}
val absences get() = _absences.toSet()
fun addAbsence(newAbsence: Absence) {
newAbsence.employee = this
_absences += newAbsence
}
#Entity
#Table(name = "absence")
data class Absence(
// ... omitted fields
) {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "employee_id")
lateinit var employee: Employee
}
}
data class EmployeeForm(
var firstName: String = "",
var lastName: String = "",
var jobType: Int = 0
) {
// not reusable
fun toEmployeeNotReusable(): Employee {
return Employee(firstName, lastName, jobType)
}
// works but hacky
fun toEmployee(): Employee {
return copyEmployee(Employee("", "", 0))
}
fun copyEmployee(employee: Employee): Employee {
return employee.copy(
firstName = firstName,
lastName = lastName,
jobType = jobType
)
}
}
While mutability would be fine, in my case, I'd be interested to know how this would be possible.
One way to avoid listing the attributes 4 times would be to declare Employee as an interface instead, and use the "mutable" version, the form, as the only data class implementing it. You would have the "read-only" view using the interface, but you would technically only use the mutable instance behind the scenes.
This would follow what Kotlin designers have done for List vs MutableList.
interface Employee {
val firstName: String
val lastName: String
val jobType: Int
}
data class EmployeeForm(
override var firstName: String = "",
override var lastName: String = "",
override var jobType: Int = 0
): Employee {
fun toEmployee(): Employee = this.copy()
fun copyEmployee(employee: Employee): Employee = this.copy(
firstName = firstName,
lastName = lastName,
jobType = jobType
)
}
However, this implies that the form has all fields of an employee, which you probably don't want.
Also, I would personally prefer what you had done in the beginning, listing twice the field would not be a problem, just write tests for your functions, and when you want to add functionality, you'll add tests for that functionality anyway.
You should be able to do this using reflection: check list of properties in Employee and EmployeeForm, call the constructor by the matching names (using callBy to handle default parameters). The drawback, of course, is that you won't get compile-time errors if any properties are missing (but for this case, any test would probably fail and tell you about the problem).
Approximate and untested (don't forget to add the kotlin-reflect dependency):
inline fun <reified T> copy(x: Any): T {
val construct = T::class.primaryConstructor
val props = x::class.memberProperties.associate {
// assumes all properties on x are valid params for the constructor
Pair(construct.findParameterByName(it.name)!!,
it.call(x))
}
return construct.callBy(props)
}
// in EmployeeForm
fun toEmployee() = copy<Employee>(this)
You can make an equivalent which is compile-time checked with Scala macros, but I don't think it's possible in Kotlin.

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"
}
}