I have received a JavaScript object in response to a remote HTTP request. I have a kotlin model (trait) that defines the various fields I expect on the object (the nullable ones are optional).
First, I want to do an is check to make sure my object is in fact of the expected type. I initially tried payload is MyModel but that doesn't work due to the way the is operator is written in kotlin.js.
Second, I want to cast to MyModel so I can get auto-complete, etc. on the object while I work with it. Normally, the is alone would be enough but since that doesn't work I need something for this problem as well.
I would like to avoid manually populating my object from a dynamic. I wouldn't mind doing this so much if I could use by Delegates.mapVal(...) but that requires a Map<String, Any?> and I don't know how to get my dynamic/Any? payload into a Map<String, Any?>.
1) We don't have structure check for is in performance reasons.
I don't sure that we need generic solution for this, but anyway I created issue about it, feel free to vote or star it to get updates.
2) is enough if you use smart cast, like:
if (payload is MyModel) {
// call MyModel members on payload
}
But don't forget about (1) :)
3) You can write something like:
class MapDynamic<out V>(val d: dynamic) {
public fun get(thisRef: Any, desc: PropertyMetadata): V {
return d[desc.name]
}
}
class Foo(data: dynamic) {
val field: Int by MapDynamic(data)
}
fun main(args : Array<String>) {
val f = Foo(object { val field = 123 })
println(f.field)
}
But it looks too verbose, but You can add additional logic for e.g. when data don't have requested field. And if You don't need custom logic I think cast is enough.
For the second part, the cast, you can do:
fun responseHandler(payload: dynamic) {
val myModel = payload as MyModel
}
or
fun responseHandler(payload: dynamic) {
val myModel: MyModel = payload
}
This will throw an NPE if payload is null, but it won't actually validate that the payload matches MyModel. In particular, you may end up with null fields/properties that shouldn't be if the payload was missing those fields/properties.
Related
Let's say I have the following class constructor:
class Car(val brand: Brand,val modelName: String, val version: Int){}
If for example, I want the version number to always start with 1. Is there a way to manipulate it in the class body to achieve this ?
Meaning:
val firstdigit:Int = abs(version).ToString().Substring(0,1)
And then parse it to Int. But how to replace the original first digit after that?
I'm just learning Kotlin and I got a bit stuck with this
Is this what you had in mind?
class Car(val brand: Brand, val modelName: String) {
val version = getNextVersion()
companion object {
private var nextVersion = 0
private fun getNextVersion(): Int {
nextVersion++
if (nextVersion.toString()[0] != '1') {
nextVersion = (10.0.pow(ceil(log10(nextVersion.toDouble())))).toInt()
}
return nextVersion
}
}
}
You already said in the comments that you want the number to increment per instance, so the caller shouldn't be providing that number in the first place really! But just generally, here's two approaches to sanitising your input parameters:
1) Make it the caller's responsibility to provide valid data
init {
require(version.toString().first() == '1') { "Needs to start with 1 thanks" }
}
require throws an IllegalArgumentException if it fails, which is the standard exception for "the value of this argument is invalid". Should the class be responsible for taking bad data and trying to "fix" it, or should the caller be handling that - and maybe not constructing an instance at all if it doesn't have valid data?
2. create a newInstance function that uses valid data, and keep the constructor private
class Thing private constructor(val number: Int){
companion object {
fun newInstance(num: Int): Thing {
return Thing(abs(num))
}
}
}
fun main() {
Thing.newInstance(-2).let { println(it.number)}
}
If it makes sense for the class itself to sanitise the input parameters, you can delegate construction to a function that takes care of that, and prevent things from calling the constructor directly with potentially bad data.
This can cause issues with e.g. serialisation libraries (which want to call the constructor directly) but in that case you could leave the constructor public, and just advise callers to call newInstance instead. Not ideal, but it's an option!
I need to display an image in an ImageView, so I need to make sure that the image url is not null first. Are these 3 options valid?
Answer data class
data class Answer(
val id: Long?,
val title: String?,
val answerImage: AnswerImage?
) {
data class AnswerImage(
val x0: AnswerImageData?,
val x1: AnswerImageData?,
val x2: AnswerImageData?
) {
data class AnswerImageData(
val id: String?,
val url: String?
)
}
}
Option 1
answer.answerImage?.let { answerImage ->
answerImage.x0?.let { answerImageData ->
answerImageData.url?.let {
//display image
}
}
}
Option 2
if (answer.answerImage?.x0?.url != null)
{
//display image
}
Option 3
answer.answerImage?.x0?.url?.let {
//display image
}
Short answer: yes.
Option 1: Would only be a good choice if you actually need to do more things with answerImage and answerImageData rather than just cast it safely. In this specific case, we don't have a use for declaring those variables explicitly. To conclude: option 1 in this case is not a very neat solution, but it does work.
Option 2: should work, because all attributes are immutable. The compiler can then deduce on the next line (inside if scope), that the url property will still be non-null.
Option 3: this is in my opinion the best one: it's the easiest one to process as a reader of the code, as you would generally finish it with code like this: .let { safeUrl -> .. }.
As specified by #Ken Van Hoeylandt all 3 options are valid, another valid option could be to use elvis operator:
fun attemptDisplayingImage(answer: Answer) {
val answerImage = answer.answerImage ?: return
val answerImageData = answerImage.x0 ?: return
val answerImageDataUrl = answerImageData.url ?: return
// display image
}
There's an interesting article about this topic here
Ken covered the answers (they're all fine and do the same thing, the last one is how the language is designed to be used really, nice and neat!) but I wanted to touch on your actual data model.
First, you say you need to check that an AnswerImageData's url isn't null. But the only reason it could be null, is because you've explicitly made it nullable, with a String? type. Is an AnswerImageData with a null url ever valid? Or does it always need to have one? I'm guessing it does, and I'm guessing it always needs an id too - so just make them non-null!
data class AnswerImageData(
val id: String,
val url: String
)
Now all your AnswerImageData objects are guaranteed to have non-null values - they're all valid in that sense, it's baked into your design. So you don't need to null check them anymore!
And the same goes for your other classes - can you have an AnswerImage with null values? This might be a trickier one, let's assume there needs to always be at least one AnswerImageData in an AnswerImage - in which case you can make the first non-null, and the others optional:
data class AnswerImage(
val x0: AnswerImageData,
val x1: AnswerImageData?,
val x2: AnswerImageData?
)
This isn't necessarily the best way to do this - I'd personally prefer a vararg parameter, or some kind of collection, so you can have an arbitrary number of AnswerImageDatas and do operations like .first(predicate) to loop over them all easily. But if you want exactly three slots, three parameters is a way to do it!
Same goes for Answer - I'm guessing that requires an id, title and answerImage - if so, don't let them be null. Enforce that valid structure through your types, it'll make your life a lot easier! And so will avoiding nullable types unless you actually need them!
I don't know if that applies to what you're doing, but it probably does, so it's worth mentioning. (This kind of thing is called *Domain-Driven Design if you want to look into it - basically enforcing the rules and structure of your business logic through the way you design your code, your types, your objects etc.)
All the answers above were great but I wanted to mention something. You declared your properties as nullable so I'm guessing you are getting them from somewhere else (from your data layer if you're familiar with clean architecture).
my recommendation is to create a domain model for your class and map the data to your domain model(which has non-null properties). this way you handle nulls in the mapper. it's cleaner and follows the separation of concerns and single responsibility principles.
interface Mapper<F, S> {
fun firstToSecond(first: F): S
fun secondToFirst(second: S): F
}
data class DataModel(
val id: Long?,
val title: String?,
val answerImage: AnswerImage?
)
data class DomainModel(
val id: Long,
val title: String,
val answerImage: AnswerImage
)
class DataToDomainMapper: Mapper<DataModel, DomainModel> {
override fun firstToSecond(first: DataModel): DomainModel {
return DomainModel(
id = first.id ?: -1,
title = first.title ?: "no title",
answerImage = first.answerImage ?: AnswerImage()
)
}
override fun secondToFirst(second: DomainModel): DataModel {
return DataModel(
id = second.id,
title = second.title,
answerImage = second.answerImage
)
}
}
this way you don't have to handle nulls anywhere else in your code. and for data validation, you can check the id not to be negative. I've shortened your models, but you get the idea
I am trying to use the public interface Function (as I learned it in Java) in Kotlin.
For this I created my method
fun foo(input: List<String>, modifier1: Function<List<String>>? = null){
}
as far I remember here I should be able to do modifier1.apply(input)
but seems like it is not possible (it is possible to do modifier1.apply{input} though)
Reading more about it I found this:
Kotlin: how to pass a function as parameter to another?
So I changed my method signature to this:
fun foo(input:String, modifier2: (List<String>) -> (List<String>){
}
Here I am able to do modifier2(input)
and I can call foo this way
service.foo(input, ::myModifierFunction)
where
fun myModifierFunction(input:List<String>):List<String>{
//do something
return input
}
So far this seems possible but it is not acceptable to have the function reference as nullable, is there any way I can do that? or use Function ?
You were using kotlin.Function instead of java.util.function.Function in your first example. Note that the latter takes 2 generic types: 1 for the incoming parameter and 1 for the resulting one.
The apply method you saw is the default Kotlin one: apply, not the one of Java's Function-interface.
If you really want to have the Java-function as nullable type the following should work:
fun foo(input: List<String>, modifier1: java.util.function.Function<List<String>, List<String>>? = null) {
modifier1?.apply(input) ?: TODO("what should be done if there wasn't passed any function?")
}
Kotlin variant for the same:
fun foo(input: List<String>, modifier1: ((List<String>) -> List<String>)? = null) {
modifier1?.invoke(input) ?: TODO("what should be done if there wasn't passed any function?")
}
Maybe also a default function, such as { it } instead of null might better suite your needs? (Java variant would be Function.identity()):
// java modifier1 : Function<List<String>, List<String>> = Function.identity()
// kotlin modifier1 : (List<String>) -> List<String> = { it }
You can make the reference nullable simply with ? — the only wrinkle is that the whole function type needs to be in parens first:
fun foo(input: String, modifier2: ((List<String>) -> List<String>)? = null) {
}
As required, modifier2 is optional; if specified, it may contain null, or it may contain a function taking and returning a list of strings.
As mentioned in another answer, kotlin.Function is not the same as java.util.function.Function — though in practice you shouldn't need to refer to either directly, as the -> notation is simpler.
If you want to pass in a function that takes List<String> as its parameter and returns nothing meaningful, the type for you is Function1<List<String>, Unit>. The method name for invoking a function is invoke(), which you could also do with just regular parentheses, if it wasn't nullable. All in all, your code could look something like this:
fun foo(input: List<String>, modifier1: Function1<List<String>, Unit>? = null) {
modifier1?.invoke(input)
}
The 1 in the typename of Function1 means that it's a one parameter function, there's also Function0, Function2, etc.
The Function type on its own is not something you can use to call that function, as it's an empty marker interface. All functions implement this regardless of how many parameters they have.
In my Ktor REST API, I receive JSON. This is conveniently deserialised for me into simple data classes by kotlinx.serialization. Before I can proceed using this data I have to apply some validation. Then I can pass it on to to other parts of the code.
I would like a clear separation between deserialised but unvalidated data, the validation itself, and validated code.
For example, I have something to the effect of:
#Serializable
data class Input(val something: Int)
fun Input.validate() {
if(something < 5) { throw WhateverException("invalid") }
}
But there's a few issues with this: When receiving an instance of Input anywhere, I can't be sure it's been validated already, so to be safe I'll call validate() again.
So, to avoid this I would like for validate() to return some version of Input that tells me the data is valid, and so that I can have method signatures in my codebase that accept validated data only.
I know I can copy the class Input to a private one called ValidatedInput and have validate() return that, but that looks like code duplication. (I will end up having dozens of classes like Input.) Also that will prevent be from having an interface specifying Input to have the validate method and have it return something like Validated<Input>
How do I design my classes and methods to clearly express this separation, and without repeating code?
If you want to make sure that Input is validated, you have to somehow put it in the type system. Since Validated<Input> is a no-go for you (I would prefer doing it this way), maybe you could turn it around by giving Input some kind of validation token as a type parameter?
For example like this:
data class Input<out Validation>(val something: Int)
sealed class Validation
object Validated : Validation()
object NotValidated : Validation()
fun Input<NotValidated>.validate(): Input<Validated> {
return if(something < 5) { throw RuntimeException("invalid") }
else Input<Validated>(something)
}
Doing it the other way (Validated<Input>) is a bit more flexible in my opinion. It also doesn't mix any code about validation into the Input class (like the validation token would). So, for example, you could do something like this:
sealed class Validated<T>
class Valid<T>(val value: T) : Validated<T>()
class Invalid<T>(val error: Exception): Validated<T>()
fun Input.validate(): Validated<Input> {
return if(something < 5) Invalid(RuntimeException("invalid"))
else Valid(this)
}
fun performAction(input: Valid<Input>) {
TODO("Do something with the valid Input")
}
fun main() {
val validated = Input(5).validate()
val result = when(validated) {
is Valid -> performAction(validated)
is Invalid -> throw validated.error
}
println(result)
}
You can move validation to Input initialization:
#Serializable
data class Input(val something: Int) {
init {
validate()
}
}
If you do it all Input instances will be valid.
In an attempt to build a DSL for validation, I am looking for ways to collect the statements inside a block/lambda with receiver. To illustrate, here is a minimal example without the actual validation logic:
data class Constraint(val hint: String)
class Validation(val constraints: List<Constraint>) {
companion object {
operator fun invoke(init: (ValidationBuilder.() -> Unit)): Validation {
return ValidationBuilder().apply(init).build()
}
}
class ValidationBuilder {
private var constraints: MutableList<Constraint> = mutableListOf()
operator fun Constraint.unaryPlus() {
constraints.add(this)
}
fun build() = Validation(constraints)
}
}
This can then be used to build a Validation like so
val validation = Validation {
+Constraint("First constraint")
val secondConstraintHint = "Second constraint"
+Constraint(secondConstraintHint)
}
I would like to get rid of the unaryPlus operator and directly collect the individual statements in the block that are evaluated to a Constraint so that I can do something like:
val validation = Validation {
Constraint("First constraint")
val secondConstraintHint = "Second constraint"
Constraint(secondConstraintHint)
}
Is that possible somehow?
To give a little bit more context, the actual result I am aiming for will look more like this:
Validation<User> {
User::firstName {
val min = 2
minLength(min) hint "Please provide a first name"
maxLength(200) // uses default hint
}
}
Well, there seems to be no straightforward solution, because Kotlin provides no way to handle an evaluated expression result that is not assigned, returned or passed anywhere.
A possible workaround is to mimic the constructor you need with a function defined for your builder:
class ValidationBuilder {
/* ... */
fun Constraint(name: String) =
full.qualified.name.of.Constraint(name).also(constraints::add)
}
Unfortunately, this will require you to duplicate all the signatures that you want to call in this way.
UPD (answering to the comment): I believe the idiomatic way for users to customize a DSL is defining their own extensions for the DSL builders:
fun ValidationBuilder.nonEmptyText(min: Int = 1, max: Int = 65.536) = TODO()
If a Constraint that comes from outside the DSL is an important use case, you can cover it with a special function (e.g. fun ValidationBuilder.constraint(...)) and let the users delegate their extensions to it.