I have a function which accepts one parameter - Any
. There is a when
block where I check all possibile types I want to support and throw in case the type is unknown.
fun toAttributeValue(value: Any): AttributeValue {
return when (value) {
...
is Map<*, *> -> {
....
}
is List<*> -> {
...
}
...
}
}
In case of a Map
, I need to make sure it's of type Map<String, Any>
, so there cannot be a key which is not a String
, and there cannot be a null
value as well and the reason for that is the fact I call some method inside this block that accepts the Map<String, Any>
as it suppose to.
I tried some options, but cannot find the best one, so for now I do:
val mappedValues = value.entries
.filter { it.key is String && it.value != null }
.associate { it.key as String to it.value!! }
require(mappedValues.size == value.size) {
"Unsupported map type"
}
method(mappedValues)
In case of a List
, I need to make sure not a single value is null
, otherwise throw an exception as well. Should I do similar way I did we the Map
, or is there a more handy way to do it?
Instead of filtering the map/list, use methods like associateBy
or map
to convert the map/list to the desired type, and throw an exception in those functions if a null/non-string is found. After all, you don't want to just remove the nulls/non-strings - your aim is to throw an exception when those are found.
// suppose 'map' is of type Map<*, *>
val stringAnyMap: Map<String, Any> = map.entries.associateBy({
(it.key as? String) ?: throw IllegalArgumentException("some key in the map is null")
}) {
it.value ?: throw IllegalArgumentException("the value associated with key '${it.key}' is null")
}
// suppose 'list' is of type List<*>
val nonNullList: List<Any> = list.map { it ?: throw IllegalArgumentException("some element in the list is null") }
Note that this also creates a copy of the map/list. This is important because even if the map/list doesn't contain nulls/non-strings at the time when you do this check, such elements could be added to the map/list later on. Therefore, a copy is necessary, so that later changes of the original map/list will not affect the Map<String, Any>
or List<Any>
that you get as a result of this operation.
Of course, if you can guarantee that the Map<String, Any>
and List<Any>
won't escape the toAttributeValue
function, and that only a single thread has access to the map/list that is passed in, you can just cast it with an unchecked cast.