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);
}
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 something like this :
import kotlin.reflect.KClass
class Quantity<T> {
/* ... */
}
class Field<T : Any> {
val type: KClass<T> get() = TODO("This is initialized, don't worry about implentation details, just know that fields know their type.")
fun initValue(value: T) {
/* Do something very useful */
}
/* Other methods */
class Template<T : Any> {
fun initFieldWithValue(value: T): Field<T> {
return Field<T>().apply {
this.initValue(value)
}
}
}
}
class ComponentClass(
val fieldsTemplates: Map<String, Field.Template<*>>
) {
inner class Instance(field: Map<String, Field<*>>)
fun new(fieldValues: Map<String, Quantity<*>>): Instance {
val fields = mutableMapOf<String, Field<*>>()
for ((fieldName, template) in fieldsTemplates) {
fields[fieldName] = fieldsTemplates
.getValue(fieldName)
.initFieldWithValue(fieldValues.getValue(fieldName) /* Here a type error */)
}
return Instance(fields)
}
}
As you might guess, this is intended to work as a 'runtime way' of creating classes that own fields (Field<T> class), each one possessing a typed value (represented by a Quantity<T>).
The problem is that this code won't compile due to the fact that the quantity retrieved from fieldValues when creating the different fields of the future Instance in the new method isn't guaranteed to be of the required type for the field it is stuffed into.
The problem is that I would need a check since filling a Field<Quantity<String>> with a Quantity<Int> is obviously not a good idea, but because of the type erasure I cannot ensure that the quantities passed in are of the good type.
Any idea ? One more thought : Fields know what their type is thanks to their type attribute, but unfortunately I can't do the same for the Quantity class...
Your initFieldWithValue function is enforcing the type of the parameter to match the type known by the Template/Field. But inside your new function, your Template is a Template<*> since you retrieve it from a collection where the values are of this type.
The point of generics is to enforce compile time checks so casting can be done safely and automatically under the hood. This is only useful when your type is known at compile time. In this case, the type is not known at compile time, so the generics are preventing your code from compiling. This is what generics are supposed to do: prevent code from compiling if the compiler cannot check that they types match.
If you want this code to compile, you should change initFieldWithValue so it doesn't enforce generics. You can instead manually check the type and throw an error or exit early if it's incorrect. It will be up to your code elsewhere to ensure you aren't mixing and matching types.
Here's an example of a version that would work. The type check it does requires the Kotlin reflection library. If you're targeting JVM only, you can use the Java Class.isAssignableFrom method instead to do this check.
class Template<T : Any> {
val type: KClass<T> get() = TODO()
/**
* #throws IllegalStateException if [value] is not of the same type
* as this Template's [type].
*/
fun initFieldWithValue(value: Any): Field<T> {
if (!value::class.isSubclassOf(type)) {
error("Invalid value type for Field type of $type")
}
return Field<T>().apply {
#Suppress("UNCHECKED_CAST") // we manually checked it above
initValue(value as T)
}
}
}
I may not have done a good job explaining the problem in the title, but here's an example:
fun main() {
acceptEnum(inferType())
}
fun acceptEnum(value: MyEnum?) {}
fun <R : Enum<R>?> inferType(): R = TODO()
enum class MyEnum {
VALUE
}
inferType() function infers its return type and bounds it to be a generic nullable enum. acceptEnum() function has a nullable enum parameter. When we write acceptEnum(inferType()), everything's fine. But if we add one more parameter to acceptEnum() and pass inferType() there again, here's what happens:
fun main() {
// first inferType() does not compile with an error:
// Type mismatch: inferred type is MyEnum? but MyEnum was expected
acceptEnum(inferType(), inferType())
}
fun acceptEnum(value: MyEnum?, value2: MyEnum?) {}
If we add more parameters, every inferType() call except the last one produces this error.
Is this a compiler bug or am I doing something wrong?
Update
Kotlin forum post: https://discuss.kotlinlang.org/t/kotlin-incorrectly-infers-nullable-enum-return-type-when-a-function-call-is-passed-as-an-argument-to-another-function/23650
Update
Kotlin issue https://youtrack.jetbrains.com/issue/KT-50232
I'm in the process of trying to port some code I wrote in Java over to Kotlin and I'm struggling mightily with some issues around generics. I quite commonly use a factory pattern in Java to return an instance of a generic interface that I want to call for a given type.
In Java I had this contract:
public Message<T extends Action> {
private List<T> actions;
..some other properties
}
And this interface:
public interface MessageConverter<T extends Action, M extends BaseModel> {
List<M> convertMessage(Message<T> message);
DataType getDataType();
}
And lastly this factory:
public class MessageConverterFactory {
//This gets populated via DI
private Map<DataType, MessageConverter> converterMap;
public <T extends Action, M extends BaseModel> MessageConverter<T, M> getMessageConverter(DataType dataType) {
return converterMap.get(dataType);
}
}
With all that in place, I was able to do things like this:
Message<T> message = mapper.readValue(messageString, type);
MessageConverter<T, M> messageConverter = messageConverterFactory.getMessageConverter(dataType);
List<M> dataModels = messageConverter.convertMessage(message);
I understand that I was abusing raw generic types in Java to an extent to make this happen, but I assumed there would be some way to still do a generic factory pattern like this.
However, no matter with I try with generic variance, star projections, etc. I cannot get Kotlin to accept any version of this code. The closest I got was down to the invocation of the generic converter's convertMessage call. It was failing because I was using star projections and attempting to restrict the type of T, but that was leading to the compiler thinking convertMessage accepts Message<Nothing>.
Is code like this possible in Kotlin? Or is there a similar alternative approach I should be using instead?
Thanks,
Jeff
The literal conversion of this to Kotlin is pretty simple, and the Java-to-Kotlin converter built in to IDEA would spit something like this out almost directly, given the equivalent Java code:
class Message<T: Action> {
private val actions: List<T> = TODO()
...
}
interface MessageConverter<T: Action, out M: BaseModel> {
fun convertMessage(message: Message<T>): List<M>
val dataType: DataType
}
class MessageConverterFactory(val converterMap: Map<DataType, MessageConverter<*, *>>) {
fun <T: Action, M: BaseModel> getMessageConverter(dataType: DataType): MessageConverter<T, M> {
return converterMap[dataType] as MessageConverter<T, M>
}
}
Note, the cast in getMessageConverter -- your Java code is doing the equivalent, without being explicit about it -- I believe the compiler would even spit out a warning about an unchecked assignment.
An alternative in Kotlin is to use an inline function with reified types to return the appropriate converter. For example, something like this:
inline fun <reified T: Action, reified M: BaseModel> converterOf(): MessageConverter<T, M> = when {
T::class == FooAction::class, M::class == BarModel::class -> TODO()
else -> error("No converter available for type ${T::class.simpleName} to ${M::class.simpleName}")
}
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
}