Best way to handle such scenario where "smart cast is imposible" - kotlin

I wonder what is the best way to handle such scenario
class Person(var name:String? = null, var age:Int? = null){
fun test(){
if(name != null && age != null)
doSth(name, age) //smart cast imposible
}
fun doSth (someValue:String, someValue2:Int){
}
}
What is the simplest way to call doSth method and making sure that name and age are nt null?
I am looking for something simple as with one variable scenario where I would simply use let
name?.let{ doSth(it) }

You can nest let as much as you like so:
fun test(){
name?.let { name ->
age?.let { age ->
doSth(name, age) //smart cast imposible
}
}
}
Another approach, that might be easier to follow, is to use local variables:
fun test(){
val name = name
val age = age
if(name != null && age != null){
doSth(name, age)
}
}
Last but not least, consider changing Person to be immutable like so:
data class Person(val name:String? = null, val age:Int? = null){
fun test(){
if(name != null && age != null){
doSth(name, age)
}
}
...
}

For the cast to be possible you have to make a local copy of the value somehow. In Kotlin this is best done explicitly:
val name = name
val age = age
if(name != null && age != null){
doSth(name, age)
}
The let function hides this behind an abstraction layer, which is not the best IMHO.

There's a nice, little lib that allows for writing let-like code with multiple variables. It's open-source and you can find it on GitHub, it's called Unwrap
Example based on readme:
unwrap(_a, _b, _c) { a, b, c ->
println("$a, $b$c") // all variables are not-null
}
All unwrap(...) methods are marked inline so there should be no overhead with using them.
By the way, this lib also allows to handle situation when there are some null variables (the nah() method).

If you want to take it a little "extreme" you could define an extension function on Pair<String?,Int?> that hides the logic for you:
fun Pair<String?,Int?>.test(block: (String, Int) -> Unit) {
if(first != null && second != null) {
block(first, second)
}
}
then, calling it will be a little more concise
(name to age).test { n, a ->
println("name: $n age: $a")
}
However, it won't really help you (since you could as well define this as a function inside the Person class itself), unless you need this kind of functionality really often throughout the whole project. Like I said, it seems overkill.
edit
you could actually make it (a little) more useful, by going fully generic:
fun <T,R> Pair<T?,R?>.ifBothNotNull(block: (T, R) -> Unit) {
if(first != null && second != null){
block(first, second)
}
}

In addition to miensol's answer there are various ways to copy property values into function variables to enable smart cast. e.g.:
Intermediary function:
class Person(var name: String? = null, var age: Int? = null) {
fun test() = test(name, age)
private fun test(name: String?, age: Int?) {
if (name != null && age != null)
doSth(name, age) //smart cast possible
}
fun doSth(someValue: String, someValue2: Int) {
}
}
Anonymous function:
class Person(var name: String? = null, var age: Int? = null) {
fun test() = (fun(name: String?, age: Int?) {
if (name != null && age != null)
doSth(name, age) //smart cast possible
})(name, age)
fun doSth(someValue: String, someValue2: Int) {
}
}
Default arguments:
class Person(var name: String? = null, var age: Int? = null) {
fun test(name: String? = this.name, age: Int? = this.age) {
if (name != null && age != null)
doSth(name, age) //smart cast possible
}
fun doSth(someValue: String, someValue2: Int) {
}
}

It is possible to define an inline method that allows you to take N parameters in order to avoid nesting lets (I'm basing my answer on this).
inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
return if (p1 != null && p2 != null) block(p1, p2) else null
}
Then
fun test() {
safeLet(name, age, {name, age ->
doSth(name, age) //smart cast
});
}

I was having the problem while assigning text to textview with same problem description.
All I did was putting double exclamation mark after the name of my textview.
For example:
var name:TextView?=null
name = findViewById(R.id.id_name)
name!!.text = "Your text"

Related

arrow ensure doesn't make value non nullable after check

have a question about ensure function, somehow it doesn't make null safe after check in either block.
What I am doing wrong, or is there a better way to ensure that value is not null except of using !!
here is my code
suspend fun checkCanConnectDirectChat(
senderId: Int?,
receiverId: Int?,
chatRoomId: Int?
) = either {
ensure(chatRoomId != null && receiverId != null) {
BadRequestExceptionResponse(message = ErrorConstants.INVALID_PAYLOAD)
}
val isSenderInChat = isUserInChat(chatRoomId, senderId).bind()
val isReceiverInChat = isUserInChat(chatRoomId, receiverId).bind()
ensure(isSenderInChat && isReceiverInChat){
BadRequestExceptionResponse(message = ErrorConstants.INVALID_PAYLOAD)
}
}
after the ensure I still see that they are nullable
Unfortunately the compiler is not "clever enough" to know that if the check inside ensure talks about null-ness, then within the block that holds. The best solution is to use the *NotNull family of functions, some of them available at Arrow, some of them available in the standard library.
In this case you would use ensureNotNull, which smart-casts the value to be not nullable.
suspend fun checkCanConnectDirectChat(
senderId: Int?,
receiverId: Int?,
chatRoomId: Int?
) = either {
ensureNotNull(chatRoomId) {
BadRequestExceptionResponse(message = ErrorConstants.INVALID_PAYLOAD)
}
ensureNotNull(receiverId) {
BadRequestExceptionResponse(message = ErrorConstants.INVALID_PAYLOAD)
}
val isSenderInChat = isUserInChat(chatRoomId, senderId).bind()
val isReceiverInChat = isUserInChat(chatRoomId, receiverId).bind()
ensure(isSenderInChat && isReceiverInChat){
BadRequestExceptionResponse(message = ErrorConstants.INVALID_PAYLOAD)
}
}

Kotlin functional find single element

I am relatively new to Kotlin and I try to overcome a special case.
I am filtering a books store and want to verify that the length of the obtained list is exactly one unit shorter than the original one. Further I need to verify that the discarded element is under a specific state. Here is my example:
fun BookStoreVerified(bookStore: BookStore): Boolean {
val specialChapter = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }.filter { it != null && it.state == Chapter.SPECIAL }
val total = bookStore.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filterNotNull()
val finalChapters = book.stores
.flatMap { it.books }
.flatMap { it.chapters }
.filter { it != null && it.state.isCorrect }
return (finalChapters.size + specialChapterFigure.size == total.size) && (specialChapter.size == 1)
}
My question is if there is a smarter way to compute the above operation. I would like to know if ander a scope like filter, map can we make reference to the previous object? ( get the length of the original list for instance ?)
You have Books where each Book contains a list of Chapters. You want to partition chapters from all the books according to some criteria.
With this in mind the partition function can be useful:
data class Chapter(val state: String)
data class Book(val chapters: List<Chapter>? = null)
fun main() {
val books = listOf(
Book(),
Book(chapters = listOf(Chapter("a"), Chapter("SPECIAL"))),
Book(chapters = listOf(Chapter("c"), Chapter("d")))
)
val (specialChs, regularChs) = books
.flatMap { it.chapters ?: emptyList() }
.partition { it.state == "SPECIAL" }
println(specialChs) // [Chapter(state=SPECIAL)]
println(regularChs) // [Chapter(state=a), Chapter(state=c), Chapter(state=d)]
}
Now that you have specialChs and regularChs, you can check whatever invariants you want.
For example:
check(specialChs.size == 1 && specialChs.first().state ==
"SPECIAL")
Edit: It is possible to abstract away the existence of null chapters inside a Book:
data class Book(val chapters: List<Chapter>? = null) {
val safeChapters: List<Chapter>
get() = chapters ?: emptyList()
}
then in your code you can flatMap { it.safeChapters } instead of .flatMap { it.chapters ?: emptyList() }

How can I decompose an object and add it to a list succintly?

If I have a class e.g.
data class Departments (
val customerFacing: Commercial,
val warehouse: Operational,
val accounting: Auditing)
Is there a more Kotlin like way to achieve the following?
val list: MutableList<Sections<*>> = arrayListOf()
if(deparments.customerFacing != null) {
list.add(Section(manager, deparments.customerFacing)
}
if(deparments.warehouse != null) {
list.add(Section(manager, deparments.warehouse)
}
if(deparments.accounting != null) {
list.add(Section(manager, deparments.accounting)
}
data class Section<T>(val manager: String, val section: T)
Based on the Departments class definition its properties can not be null, so you do not need to check for null. But if they were:
data class Departments (
val customerFacing: Commercial?,
val warehouse: Operational?,
val accounting: Auditing?)
You do not have to use if, you can use the operator ?. to do safe calls.
deparments.customerFacing?.let{list.add(Section(manager, it)}
deparments.warehouse?.let{list.add(Section(manager, it)}
deparments.accounting?.let{list.add(Section(manager, it)}
Execute if not null
As #alfcope mentioned, if departments fields can be null, they should me modeled as nullable types.
You can do the following:
val result = listOf(departments)
.flatMap { listOfNotNull(it.customerFacing, it.warehouse, it.accounting) }
.map { Session(manager, it) }

Kotlin - Overwrite Obj Props With Modified Obj Props if Not Null

TL;DR:
How do I make this less redundant (any approach that works helps)?
if (personModification.firstName != null) {person.firstName = personModification.firstName}
if (personModification.lastName != null) {person.lastName = personModification.lastName}
if (personModification.job != null) {person.job = personModification.job}
The long version: I have a simple problem. I have a class Person:
class Person (val firstName: String?,
val lastName: String?,
val job: String?)
and I have a class called PersonModification:
class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
The task is to overwrite any Person property values with PersonModification values, IF the PersonModification property isn't null. If you care, the business logic behind this is an API endpoint which modifies Person and takes a PersonModification as an argument (but can change all, or any, of the properties, so we don't want to overwrite valid old values with nulls). The solution to this looks like this.
if (personModification.firstName != null) {person.firstName = personModification.firstName}
if (personModification.lastName != null) {person.lastName = personModification.lastName}
if (personModification.job != null) {person.job = personModification.job}
I was told this is redundant (and I agree). The solution pseudocode looks like this:
foreach(propName in personProps){
if (personModification["propName"] != null) {person["propName"] = personModification["propName"]}
}
Of course, this isn't JavaScript, so it's not that easy. My reflection solution is below, but imo, it's better to have redundancy than do reflection here. What are my other options to remove the redundancy?
Refelection:
package kotlin.reflect;
class Person (val firstName: String?,
val lastName: String?,
val job: String?)
class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
// Reflection - a bad solution. Impossible without it.
//https://stackoverflow.com/questions/35525122/kotlin-data-class-how-to-read-the-value-of-property-if-i-dont-know-its-name-at
inline fun <reified T : Any> Any.getThroughReflection(propertyName: String): T? {
val getterName = "get" + propertyName.capitalize()
return try {
javaClass.getMethod(getterName).invoke(this) as? T
} catch (e: NoSuchMethodException) {
null
}
}
fun main(args: Array<String>) {
var person: Person = Person("Bob","Dylan","Artist")
val personModification: PersonModification = PersonModification("Jane","Smith","Placeholder")
val personClassPropertyNames = listOf("firstName", "lastName", "job")
for(properyName in personClassPropertyNames) {
println(properyName)
val currentValue = person.getThroughReflection<String>(properyName)
val modifiedValue = personModification.getThroughReflection<String>(properyName)
println(currentValue)
if(modifiedValue != null){
//Some packages or imports are missing for "output" and "it"
val property = outputs::class.memberProperties.find { it.name == "firstName" }
if (property is KMutableProperty<*>) {
property.setter.call(person, "123")
}
}
})
}
You can copy and paste here to run it: https://try.kotlinlang.org/
It should be pretty simple to write a 5 line helper to do this which even supports copying every matching property or just a selection of properties.
Although it's probably not useful if you're writing Kotlin code and heavily utilising data classes and val (immutable properties). Check it out:
fun <T : Any, R : Any> T.copyPropsFrom(fromObject: R, skipNulls: Boolean = true, vararg props: KProperty<*>) {
// only consider mutable properties
val mutableProps = this::class.memberProperties.filterIsInstance<KMutableProperty<*>>()
// if source list is provided use that otherwise use all available properties
val sourceProps = if (props.isEmpty()) fromObject::class.memberProperties else props.toList()
// copy all matching
mutableProps.forEach { targetProp ->
sourceProps.find {
// make sure properties have same name and compatible types
it.name == targetProp.name && targetProp.returnType.isSupertypeOf(it.returnType)
}?.let { matchingProp ->
val copyValue = matchingProp.getter.call(fromObject);
if (!skipNulls || (skipNulls && copyValue != null)) {
targetProp.setter.call(this, copyValue)
}
}
}
}
This approach uses reflection, but it uses Kotlin reflection which is very lightweight. I haven't timed anything, but it should run almost at same speed as copying properties by hand.
Also it uses KProperty instead of strings to define a subset of properties (if you don't want all of them copied) so it has complete refactoring support, so if you rename a property on the class you won't have to hunt for string references to rename.
It will skip nulls by default or you can toggle the skipNulls parameters to false (default is true).
Now given 2 classes:
data class DataOne(val propA: String, val propB: String)
data class DataTwo(var propA: String = "", var propB: String = "")
You can do the following:
var data2 = DataTwo()
var data1 = DataOne("a", "b")
println("Before")
println(data1)
println(data2)
// this copies all matching properties
data2.copyPropsFrom(data1)
println("After")
println(data1)
println(data2)
data2 = DataTwo()
data1 = DataOne("a", "b")
println("Before")
println(data1)
println(data2)
// this copies only matching properties from the provided list
// with complete refactoring and completion support
data2.copyPropsFrom(data1, DataOne::propA)
println("After")
println(data1)
println(data2)
Output will be:
Before
DataOne(propA=a, propB=b)
DataTwo(propA=, propB=)
After
DataOne(propA=a, propB=b)
DataTwo(propA=a, propB=b)
Before
DataOne(propA=a, propB=b)
DataTwo(propA=, propB=)
After
DataOne(propA=a, propB=b)
DataTwo(propA=a, propB=)
This can be solved without reflection using delegated properties. See: https://kotlinlang.org/docs/reference/delegated-properties.html
class Person(firstName: String?,
lastName: String?,
job: String?) {
val map = mutableMapOf<String, Any?>()
var firstName: String? by map
var lastName: String? by map
var job: String? by map
init {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
class PersonModification(firstName: String?,
lastName: String?,
job: String?) {
val map = mutableMapOf<String, Any?>()
var firstName: String? by map
var lastName: String? by map
var job: String? by map
init {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
fun main(args: Array<String>) {
val person = Person("Bob", "Dylan", "Artist")
val personModification1 = PersonModification("Jane", "Smith", "Placeholder")
val personModification2 = PersonModification(null, "Mueller", null)
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
personModification1.map.entries.forEach { entry -> if (entry.value != null) person.map[entry.key] = entry.value }
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
personModification2.map.entries.forEach { entry -> if (entry.value != null) person.map[entry.key] = entry.value }
println("Person: firstName: ${person.firstName}, lastName: ${person.lastName}, job: ${person.job}")
}
You can create a nice trait for this which you will be able to apply for any modification class you might have:
interface Updatable<T : Any> {
fun updateFrom(model: T) {
model::class.java.declaredFields.forEach { modelField ->
this::class.java.declaredFields
.filter { it.name == modelField.name && it.type == modelField.type }
.forEach { field ->
field.isAccessible = true
modelField.isAccessible = true
modelField.get(model)?.let { value ->
field.set(this, value)
}
}
}
}
}
Usage:
data class Person(val firstName: String?,
val lastName: String?,
val job: String?) : Updatable<PersonModification>
data class PersonModification(val firstName: String?,
val lastName: String?,
val job: String?)
Then you can try it out:
fun main(args: Array<String>) {
val person = Person(null, null, null)
val mod0 = PersonModification("John", null, null)
val mod1 = PersonModification(null, "Doe", null)
val mod2 = PersonModification(null, null, "Unemployed")
person.updateFrom(mod0)
println(person)
person.updateFrom(mod1)
println(person)
person.updateFrom(mod2)
println(person)
}
This will print:
Person(firstName=John, lastName=null, job=null)
Person(firstName=John, lastName=Doe, job=null)
Person(firstName=John, lastName=Doe, job=Unemployed)
model mapping utilities
You can also use one of the many model mapping utilities, like the ones listed in http://www.baeldung.com/java-performance-mapping-frameworks (there at least you already see some performance benchmarks regarding the different kind of model mappers).
Note that I cannot really recommend writing your own mapping utility if you do not test it thoroughly. Already seen examples where the custom mapping utility grew and grew and later on lead to strange behaviour as some corner cases weren't considered.
simplifying the != null
Otherwise, if you are not too lazy, I would rather recommend something like:
personModification.firstName?.also { person.firstName = it }
It doesn't require any reflection, is simple and still readable... somehow at least ;-)
delegated properties
Another thing that comes to my mind and somehow matches your Javascript approach are delegated properties (which I only recommend if the backed Map is a suitable model for you; actually what I am showing below is rather a delegated person map using a HashMap, which I can not really recommend, but which is quite an easy and useful way to get the Javascript look&feel; the reason why I don't recommend it: is Person a Map? ;-)).
class Person() : MutableMap<String, String?> by HashMap() { // alternatively use class Person(val personProps : MutableMap<String, String?> = HashMap()) instead and replace `this` below with personProps
var firstName by this
var lastName by this
var job by this
constructor(firstName : String?, lastName : String?, job : String?) : this() {
this.firstName = firstName
this.lastName = lastName
this.job = job
}
}
The PersonModification-class then basically looks the same. Applying the mapping would then look like:
val person = Person("first", "last", null)
val personMod = PersonModification("new first", null, "new job")
personMod.filterValues { it != null }
.forEach { key, value -> person[key] = value } // here the benefit of extending the Map becomes visible: person[key] instead of person.personProps[key], but then again: person.personProps[key] is cleaner
If you do not require that secondary constructor it's even better, then the class looks nearly as before and the properties can be set and get as before.
Thinking about it you do not really need the secondary constructor as you could still use apply and then just add the variables you are interested in (nearly as named parameters). Then the class would look similar to:
class PersonModification : MutableMap<String, String?> by HashMap() { // or again simply: class PersonModification(props : MutableMap<String, String?> = HashMap()) and replacing `this` with props below
var firstName by this
var lastName by this
var job by this
}
and instantiating it then looks as follows:
val personMod = PersonModification().apply {
firstName = "new first"
job = "new job"
}
Mapping would still be the same.
Already many people offered their solutions. But I want to offer one more:
There are interesting feature in jackson, you could try to merge json. So, you could merge src object with deserialization version of PersonModification
With it, it's possible to do something like this:
class ModificationTest {
#Test
fun test() {
val objectMapper = jacksonObjectMapper().apply {
setSerializationInclusion(JsonInclude.Include.NON_NULL)
}
fun Person.merge(personModification: PersonModification): Person = run {
val temp = objectMapper.writeValueAsString(personModification)
objectMapper.readerForUpdating(this).readValue(temp)
}
val simplePerson = Person("firstName", "lastName", "job")
val modification = PersonModification(firstName = "one_modified")
val modification2 = PersonModification(lastName = "lastName_modified")
val personAfterModification1: Person = simplePerson.merge(modification)
//Person(firstName=one_modified, lastName=lastName, job=job)
println(personAfterModification1)
val personAfterModification2: Person = personAfterModification1.merge(modification2)
//Person(firstName=one_modified, lastName=lastName_modified, job=job)
println(personAfterModification2)
}
}
Hope this will help you!
Create an extension function for Person:
fun Person.modify(pm: PersonModification) {
pm.firstName?.let { firstName = it }
pm.lastName?.let { lastName = it }
pm.job?.let { job = it }
}
fun Person.println() {
println("firstName=$firstName, lastName=$lastName, job=$job")
}
and use it like this:
fun main(args: Array <String> ) {
val p = Person("Nick", "Doe", "Cartoonist")
print("Person before: ")
p.println()
val pm = PersonModification("Maria", null, "Actress")
p.modify(pm)
print("Person after: ")
p.println()
}
Or choose one of the following:
fun Person.println() {
println("firstName=$firstName, lastName=$lastName, job=$job")
}
fun main(args: Array <String> ) {
val p = Person("Nick", "Doe", "Cartoonist")
print("Person before: ")
p.println()
val pm = PersonModification("John", null, null)
pm.firstName?.run { p.firstName = this }.also { pm.lastName?.run { p.lastName = this } }.also { pm.job?.run { p.job = this } }
// or
pm.firstName?.also { p.firstName = it }.also { pm.lastName?.also { p.lastName = it } }.also { pm.job?.also { p.job = it } }
// or
with (pm) {
firstName?.run { p.firstName = this }
lastName?.run { p.lastName= this }
job?.run { p.job= this }
}
print("Person after: ")
p.println()
}
It is nothing fancy, but it hides the complexity of mutating Person from the outside world.
class Person(
var firstName: String?,
var lastName: String?,
var job: String?
) {
fun modify(p: PersonModification){
p.firstName?.let { firstName = it }
p.lastName?.let { lastName = it }
p.job?.let { job = it }
}
}
class PersonModification(/* ... */)

Check for null in map function in Kotlin

I'm new to Kotlin and I want to map an object (ProductVisibility) base on another one (fmpProduct). Some object can't be converted so I need to skip them on some condition.
I wanted to know if there's a better way to do this than what I did with the filter and the "!!" I feel that it's hacked. Am I missing something ?
val newCSProductVisibility = fmpProducts
.filter { parentIdGroupedByCode.containsKey(it.id) }
.filter { ProductType.fromCode(it.type) != null } //voir si on accumule les erreus dans une variable à montrer
.map {
val type = ProductType.fromCode(it.type)!! //Null already filtered
val userGroupIds = type.productAvailabilityUserGroup.map { it.id }.joinToString(",")
val b2bGroupIds = type.b2bUserGroup.map { it.id }.joinToString { "," }
val b2bDescHide = !type.b2bUserGroup.isEmpty()
val parentId = parentIdGroupedByCode[it.id]!! //Null already filtered
CSProductDao.ProductVisibility(parentId, userGroupIds, b2bGroupIds, b2bDescHide)
}
edit: updated the map access like comment suggested
Use mapNotNull() to avoid the filter()s and do everything in the mapNotNull() block, then the automatic typecast to non-null type works.
Example:
fun f() {
val list = listOf<MyClass>()
val v = list.mapNotNull {
if (it.type == null) return#mapNotNull null
val type = productTypeFromCode(it.type)
if (type == null) return#mapNotNull null
else MyClass2(type) // type is automatically casted to type!! here
}
}
fun productTypeFromCode(code: String): String? {
return null
}
class MyClass(val type: String?, val id: String)
class MyClass2(val type: String)