Praful Bagai
Praful Bagai

Reputation: 17382

Kotlin - Type mismatch: inferred type is Any? but Boolean was expected

I'm trying my hands on Kotlin. Being from a Python background is really giving me a tough time to get the knack of the Kotlin syntax. I'm trying to do a simple dictionary (Mutable Map) operation. However, its giving me exceptions.

This is what I tried. Kotlin compiler

Adding the code snippet for reference.

fun main() {
    val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
    val packageName = "amazon"
    val currentTime = 23454321234
    if(openActivityMap.containsKey(packageName)){ 
        if(openActivityMap[packageName]?.get("isAlreadyApplied")){
            if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600){
                openActivityMap[packageName]?.put("isAlreadyApplied", false)
            }
        }
        else{
            openActivityMap[packageName]?.put("isAlreadyApplied", false)
        }
    }
}

Upvotes: 3

Views: 8866

Answers (5)

Joffrey
Joffrey

Reputation: 37700

I'm a bit late to the party, but I'd like to point out another solution here.

As I commented on the OP, heterogeneous maps with fixed string keys like this are usually better expressed with classes in Kotlin. For instance, in your case, the class for your main map's values could be the following:

data class PackageInfo(
    var isAlreadyApplied: Boolean,
    var lastAppliedAt: Long,
)

(you could obviously add more properties if need be)

This would save you all the casts on the final values.

Another point I'd like to make is that if you access the value for a key anyway, you don't need to check up front the existence of the key with containsKey. Maps return null for keys that are not associated with any value (this is why you need to check for null after getting the value).

The compiler cannot see the correlation between containsKey and the subsequent get or [] access. However, it's smart enough to understand a null check if you simply get the value first and then check for null.

This always applies unless you want to tell the difference between keys that aren't in the map and keys that are in the map but associated to null values (which is quite rare).

All in all, I would write something like that:

fun main() {
    val openActivityMap = mutableMapOf<String, PackageInfo>()
    val packageName = "amazon"
    val currentTime = 23454321234
    val packageInfo = openActivityMap[packageName]
    if (packageInfo != null) { // the key was found and the value is smart cast to non-null in the next block
        if (packageInfo.isAlreadyApplied) {
            if ((packageInfo.lastAppliedAt - currentTime) > 3600) {
                packageInfo.isAlreadyApplied = false
            }
        } else {
            packageInfo.isAlreadyApplied = false
        }
    }
}

data class PackageInfo(
    var isAlreadyApplied: Boolean,
    var lastAppliedAt: Long,
)

Upvotes: 4

thinkgruen
thinkgruen

Reputation: 1

maybe you can try something like this

        if (openActivityMap.containsKey(packageName)) {
            val packageMap = openActivityMap[packageName]!!
            val applyRequired = (packageMap["lastAppliedAt"] as Long - currentTime) > 3600
            packageMap["isAlreadyApplied"] = packageMap.containsKey("isAlreadyApplied") && !applyRequired
        }

btw. do you really want to have lastAppliedAt to be in te future? otherewise it will never be > 3600

Upvotes: 0

XepterX
XepterX

Reputation: 1011

There's a problem with the 2 statement below

if(openActivityMap[packageName]?.get("isAlreadyApplied"))

if((openActivityMap[packageName]?.get("lastAppliedAt") - currentTime) > 3600)

As we all know, an IF statement requires a boolean value for it's param. The types of both statement are unknown at compilation time as they are of a Generic type, Any. As such,

  1. openActivityMap[packageName]?.get("isAlreadyApplied") could be a null or of type Any (Not Boolean).
  2. openActivityMap[packageName]?.get("lastAppliedAt") could be a null or of type Any (an Int was expected here for computation).

This would throw compilation errors as the compiler does not know the types to go with. What could be done is to cast to it's proper types.

Solution

openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean ?: false

((openActivityMap[packageName]?.get("lastAppliedAt") as Int ?: 0) - currentTime)

Giving a default value if it's null.

Upvotes: 1

unigeek
unigeek

Reputation: 2826

I would recommend writing tests first and working in small increments, but this should fix your compilation issues:

fun main() {
    val openActivityMap = mutableMapOf<String, MutableMap<String, Any>>()
    val packageName = "amazon"
    val currentTime = 23454321234
    if(openActivityMap.containsKey(packageName)){ 
        if(openActivityMap[packageName]?.get("isAlreadyApplied") as Boolean){
            if((openActivityMap[packageName]?.get("lastAppliedAt") as Long - currentTime) > 3600){
                openActivityMap[packageName]?.put("isAlreadyApplied", false)
            }
        }
        else {
            openActivityMap[packageName]?.put("isAlreadyApplied", false)
        }
    }
}

EDIT: Also I prefer to avoid nullable variables and mutable objects in general, but I suppose there's an exception to every rule.

Upvotes: 3

Submersed
Submersed

Reputation: 8870

Couldn't you just declare your Map<String, Any> to return a Boolean instead of Any? So,

val openActivityMap = mutableMapOf<String, MutableMap<String, Boolean>>()

It looks like you're trying to use your second Map to store both Booleans and Ints, which is complicating the logic. You'll need to typecast if you decide to approach it without Typing.

Upvotes: 1

Related Questions