Asq
09/15/2021, 7:06 PMinline fun <reified R, reified S> reifiedGenericIsSame(r: R, s: S): Boolean = (r is S) && (s is R)
interface FooBar<out A: Any, out B: Any>
typealias FBOI<A> = FooBar<Int,A>
typealias FBOS<A> = FooBar<String,A>
and the following:
open class Base<out A: Any, out B: Any>(val a: A, val b: B): FooBar<A, B>
class SpecialOfInt<out B: Any>(b: B) : Base<Int, B>(1, b)
class SpecialOfStr<out B: Any>(b: B) : Base<String, B>("1", b)
// val soitd: FBOI<Int> = SpecialOfInt(2)
// val sostd: FBOS<Int> = SpecialOfStr(2)
val soi = SpecialOfInt(2)
val sos = SpecialOfStr(2)
//
// EDIT
//
val soitd: FBOI<Int> = soi
val sostd: FBOS<Int> = sos
then this holds:
reifiedGenericIsSame(soi, sos) shouldBe false // passes
reifiedGenericIsSame(soi, sos) shouldBe true // FAILS
reifiedGenericIsSame(soitd, sostd) shouldBe false // FAILS
reifiedGenericIsSame(soitd, sostd) shouldBe true // passes
which 🤞is VERY counterintuitive, as it appears a typealias (allegedly syntax sugar) is changing a result semantically. I suppose type erasure side effects may be invoked as the mechanics, but this question is about the intrinsic behavior. Could please anyone provide me with great kotlin wisdom?mkrussel
09/15/2021, 7:32 PMis operator doesn't take into account the generics of the type being checked so listOf("foo") is List<Int> will be true. That's why you get true for soitd and sostd.
On the other hand soi and sos are different types, so the is check fails as expected.mkrussel
09/15/2021, 7:33 PMsoi and sos are FooBar so the is check passes. It doesn't mater that one is FooBar<Int,A> and the other FooBar<String, A>Asq
09/15/2021, 7:36 PMreifiedGenericIsSame(soi, sos) is falseAsq
09/15/2021, 7:37 PMFooBar . Also soitd and sosts are different types. If I set
val soitd: FBOI<Int> = soi
val sostd: FBOS<Int> = sos
the outcome is identical.mkrussel
09/15/2021, 7:42 PMsoi and sos are different types so the method correct returns false.
soitd and sostd are also different types, but are declared as the same type FooBar. Since the typealias is just syntax sugar, both R and S in reifiedGenericIsSame are both FooBar. Bot soitd and sostd are of types that are subtypes of FooBar so the function returns truemkrussel
09/15/2021, 7:43 PMS and R are determined at compile time and not runtime.Asq
09/15/2021, 7:51 PMsoi as well as the type of soitd IIUC is SpecialOfInt, i.e. Foobar<Int, A> . The type of sos and sostd is SpecialOfStr, i.e. FooBar<String,A> . The difference between soi and sos is correctly resolved. The difference between soitd and sostd is not. The only delta is the typedef FBOI or FBOS . Nothing else is different. I have difficulty reconciling your explanation with my understanding.mkrussel
09/15/2021, 8:08 PMreifiedGenericIsSame(soitd, sostd) gets inlined as the following
(soitd is FBOS<Int>) && (sostd is FBOI<Int>)
which gets translated to (soitd is FooBar<String,A>) && (sostd is FooBar<Int,A>)
after type erasure it becomes (soitd is FooBar) && (sostd is FooBar)
That is true because all the variables are of type FooBarmkrussel
09/15/2021, 8:09 PMS and R are determined at compile timeAsq
09/15/2021, 8:22 PMreifiedGenericIsSame(soi, sos) gets inlined (eventually?) as (soi is FooBar<String,A>) && (sos is FooBar<Int,A>) ? If so, why would the eventual result be different? (soi and sos are correctly recognized as different types, soitd and sostd are incorrectly recognized as the same type). Or could it possibly be that reifiedGenericIsSame(soi, sos) gets inlined (through some different mechanism?) as (soi is SpecialOfStr<A>) && (sos is SpecialOfInt<A>) ? If so, the typealias is at some fault, in addition to type erasure.
Q: Should the typealias be (always?) ignored by the compiler in a is comparison? It should be, after all, just sugar.mkrussel
09/15/2021, 8:47 PMis and the real value will be used.
The difference between reifiedGenericIsSame(soi, sos) and reifiedGenericIsSame(soitd, sostd) are the declared types of the references (not the objects they point to).
reifiedGenericIsSame(soitd, sostd) will use is FBOI and is FBOS , These are type aliases to the same type so the checks always pass.
reifiedGenericIsSame(soi, sos) will use is SpecialOfInt and is SpecialOfStr
because those are the types of the references at compile time.mkrussel
09/15/2021, 8:48 PMAsq
09/15/2021, 9:02 PMfun <A, B> isSameType(a: A, b: B): Boolean = a?.let{ outer -> outer::class == b?.let{ it::class } } ?: false
the above, in the example provided, behaves in a manner sometimes contradicting reifiedGenericIsSame to the extent that isSameType(soi, sos) and isSameType(soitd, sostd) behave in a consistent manner.mkrussel
09/15/2021, 9:06 PMis operator is checking is a relationship. So a SpecialOfInt is a FooBar so is operator returns true. Type alias will not let you change the type of the object.
val foo = 5
foo is Int // true
foo is Number // true
foo is Any // true
foo is Int? // true
foo is String // falsemkrussel
09/15/2021, 9:08 PMmkrussel
09/15/2021, 9:11 PMsoitd is a reference that can refer to any FooBar<Int,A> It happens to be referencing an object of type SpecialOfIntAsq
09/15/2021, 9:17 PM