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 false
Asq
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 FooBar
mkrussel
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 // false
mkrussel
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 SpecialOfInt
Asq
09/15/2021, 9:17 PM