I can only think of one solution. I guess all of those classes share some common type which defines id, value and color. If thats the case you can simply cast to that shared super type.
If not but it’s your own code you could add an interface to all of those types that defines the required data.
Well AFAIK contracts are not released yet. (They work in 1.3M2 I think and then only if enabled, but I’m not sure). So this would not work, and you can’t just remove the contract from your version as it’s required for the smart cast. Except for that it looks like it should work
Yes that would be great, but all these class are generate without super type and I don’t want to change these class because I will lose the change each time I will regenerate them
This is basically the same as code in original question. Instead I would prefer the solution by @roryaburks.
@cepalle, can you redefine your classed delegating to these classes. Then you may define the hierarchy of your classes or encapsulate them into sealed class. Would this work for you?
Multiple returns as default values… It’s more a hack than code that express what it does (and code should always express what it does). That’s why I would not recommend @roryaburks’s solution.
And if you have no control on generated classes, you are ****ed up: you need either specific code for each classes, or you can use reflexion (spoiler: you shouldn’t you reflexion). This is a weakness of design of the language : you cannot implement a contract somewhere else than in the class it self… But thatis the subject of another topic.
I often did it like this (, now I would use my solution):
return when (data) {
is DelTodoSubSubscription.DelTodo -> Todo(
id = data.id ?: return null,
value = data.value ?: return null,
color = data.color ?: return null
)
}
Another solutution would be to create a method like this:
return when (data) {
is DelTodoSubSubscription.DelTodo -> createNullableTodo(id, value, color)
}
fun createNullableTodo(id: String?, value : Any?, color: Color?) : Todo? = Todo(
id = id ?: return null,
value = value ?: return null,
color = color ?: return null
)
(You can remove Todo?, but i think it’s clearer with)
Thank you all for your answers, with your answers, I decided to change my code for not use when, the new part of the code I had asked is a bit different and looks like this now:
private fun tryMakeTodo(id: Int?, value: String?, color: String?) =
if (id != null && value != null && color != null)
Todo(id, value, color)
else null
fun DelTodoSubSubscription.DelTodo.toTodo() = tryMakeTodo(id, value, color)
fun AddTodoSubSubscription.AddTodo.toTodo() = tryMakeTodo(id, value, color)
fun UpdateColorTodoSubSubscription.UpdateColorTodo.toTodo() = tryMakeTodo(id, value, color)
fun TodosQuery.Todo.toTodo() = tryMakeTodo(id, value, color)
It’s just a boilerplate shortcut that one is likely going to run into frequently when coalescing nulls. Just as the first tome someone encounters a?.b() is going to be confused and think the code “doesn’t express what it does”, the same is true of val notNull = nullableThing ?: return null and DependsNotNull( nullableThing ?: return null). But as this becomes a frequent use case scenerio and considering that ?: return xyz which types as Nothing is functionality that was explicitly added in Kotlin, it’s probably OK to use it and assume Kotlin users know what it does.
Having three of them in one line does get a bit ugly (should probably prefer @tieskedh’s formatting), but I think it’s pretty clear what it does.