Taking my first steps in Kotlin, I'm struggling to find the correct signature for a function that receives an instance of a known class along with the desired output class and then looks in a map of converter lambdas whether the conversion can be done.
Here's an example for Long:
private fun <T> castLong(value: Long, clazz: Class<out T>): T {
// map lookup removed for simplicity
return when (clazz) {
String::class.java -> { value.toString() }
else -> { throw IllegalArgumentException("Unsupported Cast") }
}
}
Where T is the class of the desired return value - let's say String. One should be able to call castLong(aLongValue, String::class.java) and receive an instance of String.
But the compiler says:
Type mismatch: inferred type is String but T was expected
This seems like it should be possible as it is quite straightforward so far but even playing around with reified and other constructs didn't yield any better results.
It happens because it can't smart cast String to T, you have to manually cast it.
Furthermore, since you said you are taking your first steps in Kotlin, I leave here two other "advices" not strictly related to your question:
you can get the class of T making it reified
the brackets of a case using when aren't necessary if the case is one line
private inline fun <reified T> castLong(value: Long): T {
// map lookup removed for simplicity
return when (T::class.java) {
String::class.java -> value.toString()
else -> throw IllegalArgumentException("Unsupported Cast")
} as T
}
Related
I am trying to convert this piece of java code to kotlin
public int compare1(Comparable c, Object o) {
return c.compareTo(o);
}
to kotlin code:
fun compare1(c: Comparable<*>, o: Any?): Int {
return c.compareTo(o)
}
But get error
Type mismatch: inferred type is Any? but Nothing was expected
Any reason why this error occurs? Thanks
This code in Java shows a warning, because the compiler can't know if provided comparable can compare itself to provided object. Compiler still allows this, but it doesn't guarantee it won't throw an exception at runtime.
Its equivalent in Kotlin is either:
fun compare1(c: Comparable<Any?>, o: Any?): Int {
return c.compareTo(o)
}
With this code you'll have to do unchecked casts when calling the function. Or alternatively:
fun compare1(c: Comparable<*>, o: Any?): Int {
return (c as Comparable<Any?>).compareTo(o)
}
Note it doesn't solve the problem. You can call this function passing an integer and a string and then you will get an exception. So use this code only if the logic of your application guarantees you always pass matching objects to the function.
Even better, try to redesign your code to use generics in a type-safe manner. In that case your function would become:
fun <T> compare1(c: Comparable<T>, o: T): Int {
return c.compareTo(o)
}
This function is type-safe, so it doesn't allow using comparables with incorrect types. It may not work as a direct replacement of your Java function though, as the original function didn't care about the type safety. You may need to redesign other parts of your code to use this function.
I have a generic function to fetch/get any list out of the SharedPreferences. However, when I wanted to test, that it does not work, when I saved a list of say, Messages and ask for a list of say, Ints, it still worked. It just ignored the type I precised and returned a List of JsonObjects. When I debugged the whole code, I found, that apparently the function does not care about the inferred class type. I´ll first put here the code, so I can explain the problem:
fun <T> getListFromPreferences(preferences : SharedPreferences, key : String)
: MutableList<T> {
val listAsString = preferences.getString(key, "")
val type: Type = object : TypeToken<List<T>>() {}.type
val gson = SMSApi.gson
return gson.fromJson<ArrayList<T>>(listAsString, type)
?: ArrayList()
}
So, what I would expect, was, that when I call the function like this:
PreferenceHelper.getListFromPreferences<Message>(preferences, TEST_KEY)
the "type" variable in the above code should return List. However the result the debugger shows me is: java.util.List<? extends T>
I have absolute no idea, why the inferring does not work, but I´d really like it to work to ensure, what I am requesting is actually what I get, for obvious reasions.
Does anybody know a reason and a solution for this weird behaviour?
Due to type erasure, actual type information about T is lost, so basically this method returns List<Any?> (even if you pass Int as T).
To preserve the actual type, you need to declare this method with reified parameter:
inline fun <reified T> getListFromPreferences(preferences : SharedPreferences, key : String)
: MutableList<T> {
//...
}
I am not sure if 'hard-failing' is the right word, but here is the problem I am facing. And it's taken me quite some time to reproduce this to the smallest possible example, so here it goes:
class BaseParameterizedType<T>
fun <U: BaseParameterizedType<*>> getSpecific(clazz: KClass<in U>) : U {
TODO()
}
fun example(arg: KClass<out BaseParameterizedType<*>>)) {
getSpecific(arg.innerType)
}
Ok, so the code above fails at the 'TODO', but if it wasn't there and if the function returned normally, then it definitely fails with a null pointer exception. I tried hard to figure out what was going wrong, so I turned to the decompiled Java code (from the kotlin bytecode):
public static final void example(#NotNull KClass arg) {
Intrinsics.checkParameterIsNotNull(arg, "arg");
getSpecific(arg.getInnerType());
throw null; // <-- The problem
}
If I change the function signature of getSpecific(clz: KClass<in U>) : U to any of these forms:
getSpecific(clz: KClass<out U>) : U
getSpecific(clz: KClass<U>) : U
getSpecific(clz: KClass<in U>) : BaseParameterizedType<*>
or even the function to example(arg: KClass<out BaseParameterizedType<*>) or example(arg: KClass<BaseParameterizedType<*>>), then the generated code is:
public static final void example(#NotNull KClass arg) {
Intrinsics.checkParameterIsNotNull(arg, "arg");
getSpecific(arg.getInnerType());
}
Now, let's say at the call-site, I change it to:
getSpecific(BaseParameterizedType::class)
then this also DOES NOT generate the throw null clause. So, I'm guessing this has something to do with kotlin assuming that this cast will always fail or that there is indeterminate information available to make the inference?
So, we know that arg.innerType is KClass<out BaseParameterizedType<*>> and we use it at a site accepting KClass<in BaseParameterizedType<*>>, so why isn't U inferred to BaseParamterizedType<*>>. That is literally the only type that will ever match.
At the same time, I think just generating a throw null statement is unbelievably difficult to debug. The stacktrace would just point to the line where there is getSpecific and good luck figuring out where the null pointer exception came from.
This is a known issue regarding the the type inference corner case handling when the inferred type is Nothing (and it is in your case):
The inference behaves in this way because of a coercion attempt for the projections KClass<in U> and KClass<out BaseParameterizedType<*>>.
Basically, an out-projected type at the same time means in Nothing (because the actual type argument can be any of the subtypes, and nothing can be safely passed in). So, to match KClass<out BaseParameterizedType<*>> with KClass<in U> the compiler chooses U := Nothing, implying that the function call returns Nothing as well.
Remark: a Foo<out Any> projection cannot match Foo<in T> with T := Any, because the actual type argument of the value passed for Foo<out Any> can be, for example, Int. Then, if Foo<T> accepts T in some of its functions, allowing the aforementioned match will also allow you to pass Any instances to where Foo<Int> does not expect them. Actually, in Nothing becomes the only way to match them, because of the unknown nature of the out-projected type.
After that, for a Nothing-returning function call, the compiler inserts that throw null bytecode to make sure the execution does not proceed (evaluating a Nothing-typed expression is supposed to never finish correctly).
See the issues: KT-20849, KT-18789
Just as #hotkey mentioned, out means in Nothing and Nothing will throw null.So I do some tests like this:
fun main(args: Array<String>) {
tryToReturnNothing()
}
fun tryToReturnNothing(): Nothing{
TODO()
}
Generate ->
public static final void main(#NotNull String[] args) {
Intrinsics.checkParameterIsNotNull(args, "args");
tryToReturnNothing();
throw null; // here
}
#NotNull
public static final Void tryToReturnNothing() {
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
}
Considering the type of null is Nothing?, we can return Nothing? instead of Nothing. So I change U into U?, and then the throw null clause disappear:
fun <U: BaseParameterizedType<*>> getSpecific(clazz: KClass<in U>) : U? { // see here: change U to U?
TODO()
}
fun example(arg: KClass<out BaseParameterizedType<*>>) {
getSpecific(arg)
}
Generate ->
#Nullable
public static final BaseParameterizedType getSpecific(#NotNull KClass clazz) {
Intrinsics.checkParameterIsNotNull(clazz, "clazz");
throw (Throwable)(new NotImplementedError((String)null, 1, (DefaultConstructorMarker)null));
}
public static final void example(#NotNull KClass arg) {
Intrinsics.checkParameterIsNotNull(arg, "arg");
getSpecific(arg);
}
val specials:Map<String, (Any)->Unit> = mapOf(
"callMe1" to {asParam1()},
"callMe2" to {asParam2()}
)
fun asParam1(num:Int) {
println(num)
}
fun asParam2(text:String) {
println(text)
}
fun caller() {
specials["callMe1"]?.invoke("print me")
specials["callMe2"]?.invoke(123)
}
fun main(args: Array<String>) {
caller()
}
My requirement is simple, I want to save the function asParam1 and asParam2 as a value in the variable specials. And invoke it later on by fetching the value from a Map.
However, the compiler doesn't like it:
Error:(1, 40) Type inference failed. Expected type mismatch: inferred
type is Map Unit> but Map Unit> was
expected
Error:(1, 69) No value passed for parameter num
Error:(1, 96) No value passed for parameter text
While this task is pretty simple in a weak typed language, I don't know how to do in Kotlin. Any help would be welcome. Thanks!
The correct syntax is "calllme" to ::asParam1.
But then the signatures will be wrong because the Map expects type (Any)->Unit and yours have (Int)->Unit and (String)->Unit. Here is an example that does not produce the error:
val specials:Map<String, (Any)->Unit> = mapOf(
"callMe1" to ::asParam1,
"callMe2" to ::asParam2
)
fun asParam1(num:Any) {
if(num is Int) println(num)
}
fun asParam2(text:Any) {
if(text is String) println(text)
}
fun caller() {
specials["callMe2"]?.invoke("print me")
specials["callMe1"]?.invoke(123)
}
Keep in mind, your code for the caller has special knowledge about how to call each of your functions (i.e., the correct parameter types), but the compiler does not have this same knowledge. You could accidentally call asParam1 passing a String instead of an Int (which is what your caller function was doing, I fixed it in my example) and that is not allowed. Which is why I changed the signatures of both asParam* to accept Any parameter, and then validated the expected type in each function (ignoring bad types).
If your intent is to pass integers in addition to strings to asParam2(), then change the body to test for both Int and String and convert the integer to a string.
When you write { asParam1() }, you create a lambda with an executable code block inside it, so you need to properly call the function asParam1(...), which requires an Int argument.
So, the first change you need to make is: { i -> asParam1(i) }.
But this code will still not pass the type checking, because, matching the type of the map, the lambda will be typed as (Any) -> Unit (the values in the map should all be able to accept Any, and a function that expects a narrower type cannot be a value in this map).
You then need to convert the Any argument to Int to be able to invoke the function: { i -> asParam1(i as Int) }
Finally, the map will look like this:
val specials: Map<String, (Any) -> Unit> = mapOf(
"callMe1" to { i -> asParam1(i as Int) },
"callMe2" to { s -> asParam2(s as String) }
)
The invocation stays unchanged, as in your code sample.
The function reference syntax (::asParam1) would allow you to reference a function that already accepts Any, it would not implicitly make the conversion described above. To use it, you would have to modify your functions to accept Any, as in #Les's answer.
I have class:
class Generic<T : SuperType>() { ... }
And this code is't correct, but cast s to type T:
fun typeCheck(s: SuperType) {
when(s) {
is T -> // Do something
}
}
If use: s as T - this cast will show warning (unsafe cast).
How check that s is T type?
If you need to check if something is of generic type T you need to to have an instance of Class<T> to check against. This is a common technique in Java however in Kotlin we can make use of an inlined factory method that gets us the class object.
class Generic<T : Any>(val klass: Class<T>) {
companion object {
inline operator fun <reified T : Any>invoke() = Generic(T::class.java)
}
fun checkType(t: Any) {
when {
klass.isAssignableFrom(t.javaClass) -> println("Correct type")
else -> println("Wrong type")
}
}
}
fun main(vararg args: String) {
Generic<String>().checkType("foo")
Generic<String>().checkType(1)
}
Generic types are not reified on the JVM at runtime, so there's no way to do this in Kotlin. The warning is correct because the compiler can't possibly generate any instruction that will fail when the cast is done, so the cast is unchecked, meaning that the program may or may not break at some point later instead.
A related feature which might be of use is reified type parameters in inline functions. Classes can't have reified type parameters though, so if you elaborate a bit more on your use case, I can try helping you achieve what you seem to need.
I know that I'm kinda late to this thread, but I just want to recap on the answer provided by Alexander Udalov.
It is, indeed, impossible to determine the type of a generic parameter in Kotlin unless you're using inline functions and declaring the generic type as reified.
Not sure if I'll be able to answer this question entirely and accurately, but I feel like my contribution might still be valuable for someone who is attempting to do just that. So let's say you have a few data classes, and you want to check which type you're dealing with.
You could use a function like that:
inline fun <reified T> checkType() = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
however, functions that call it must also be inline, so you might have to write something like
inline fun <reified T> someOtherFunction(data: T) {
checkType<T>
}
however, if you cannot allow for an inline function (let's say in an interface!), you can kinda 'cheat' the system by saying, for example
class AmazingTypes {
inline fun <reified T> checkType(genericParameter: T) = when (T::class) {
TypeA::class -> println("TypeA")
else -> println("Type not recognized")
}
}
fun myAwesomeMethod(someParameter: Any) {
val amazingClass = AmazingClass()
amazingClass.checkType(someParameter)
}
This is also example.
inline fun <reified T: ApiResponse> parseJson(body: String): T {
// handle OkResponse only
val klass = T::class.java
if (klass.isAssignableFrom(OkResponse::class.java)) {
return T::class.java.newInstance()
}
// handle others
return gson.from(body, T::class.java)
}