Why am I getting a type mismatch when I try to return a value for a checked general parameter?

The following code is "Happy Halloween!" , 42 , etc. marked as Type Mismatch. (Required: T, Found: String (or Int)), but should the compiler not conclude that the return value is of the correct type from the type check?

 interface Type<T> class StringType() : Type<String> class IntType1() : Type<Int> class IntType2(val a: Int, val b: Int) : Type<Int> fun <T> something(type: Type<T>): T = when (type) { is StringType -> "Happy Halloween!" is IntType1 -> 42 is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b else -> throw IllegalArgumentException() } 
+5
source share
4 answers

When the compiler applies type erasure, the return type will be determined. So, let's say you use String ... your method will look like this:

 fun something(type: Type<String>): String = when (type) { is StringType -> "Happy Halloween!" is IntType1 -> 42 //Wrong: you must return String! is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b else -> throw IllegalArgumentException() } 

This thing: you must know your return type at compile time . If you do not know this, you should report this to the compiler:

 fun <T> something(type: Type<T>): Any = when (type) { is StringType -> "blabla" is IntType1 -> 42 is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b else -> throw IllegalArgumentException() } 

Sorry this code, it's not that you are jumping, you are going to throw after the method returns ...

But you can do it:

 fun <T> something(type: Type<T>): T = when (type) { is StringType -> type.b //is IntType1 -> 42 remove this! is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b else -> throw IllegalArgumentException() } 

Assuming type.a and type.b are parameterized as T. Then your code will work fine.

0
source

If I simplify your example a bit:

 interface Type<T> fun <T> something(type: Type<T>): T = when (type) { is Type<String> -> "Happy Halloween!" else -> throw IllegalArgumentException() } 

the compiler now complains that it: cannot check for instance of erased type

So the problem is that due to erasing styles at runtime there is no difference between Type<String> and Type<Int> , so the compiler will not allow this.

You can try using something like Gson TypeToken<T> or Jackson TypeReference<T> docs link to this blog post explaining the idea: http://gafter.blogspot.ca/2006/12/super-type-tokens.html

0
source

The is statement, like the Java instanceof statement, is executed at run time .

So, at compile time, the compiler does not know the actual type, so you get a compilation error.

Here is another simple example:

 fun <T>f(t: T): T { if (t is Int) return 3 // compilation error else return t } 
0
source

You can write this:

 interface Type<T> class StringType() : Type<String> class IntType1() : Type<Int> class IntType2(val a: Int, val b: Int) : Type<Int> inline fun <reified T> something(type: Type<T>): T { val result = when(type) { is StringType -> "Happy Halloween" is IntType1 -> 42 is IntType2 -> type.a * type.a + type.b * type.b + type.a * type.b else -> throw IllegalArgumentException() } return if (result is T) result else throw Exception() } 

Doing the following:

 fun main(args: Array<String>) { println(something(StringType())) println(something(IntType1())) println(something(IntType2(2, 3))) } 

You will get this result:

 Happy Halloween 42 19 

Learn more about the reified built-in functions and parameters here: Reified type parameters .

0
source

Source: https://habr.com/ru/post/1259073/


All Articles