native
native copied to clipboard
Non-reifiable types and `obj.isInstanceOf(type)`
However it's important to note that instanceof requires a reifiable type. If we simply ignore this, users will get false positives because of generic type erasure.
One way is to require a JClass instead, so we have obj.isInstanceOf(type.jClass). But it's still not obvious that list.isIntanceOf(ArrayList.type(JInteger.type).jClass) can be true when list is in reality ArrayList<String>!
So either
- We want to introduce the functionality but warn people about type erasure in docs
- Or introduce yet another
JReifiableTypetype tree (and accessing them byFoo.reifiableType) and only accept that forisInstanceOf.
Wdyt @dcharkes?
- We want to introduce the functionality but warn people about type erasure in docs
I presume developers experienced with Java/Kotlin will know about type erasure. So mentioning it in docs should ring a bell for those users. (I remember the days I first ran into this... 😅)
Or introduce yet another
JReifiableTypetype tree (and accessing thembyFoo.reifiableType) and only accept that forisInstanceOf.
This would be nice indeed. It forces users to "read the docs" by power of the type checker. How would this work exactly?
It's still not obvious that
list.isIntanceOf(ArrayList.type(JInteger.type).jClass)can betruewhenlistis in realityArrayList<String>!
What is reality here? The static type or the dynamic type? At runtime there is no type argument, so basically it's always ArrayList<Object> (where Object is nullable) at runtime.
Shouldn't this issue be named "Non-reifiable types and instanceOf"? And JNonReifiedType + Foo.nonReifiedType? Or am I misunderstanding what you are trying to achieve here?
Maybe it should just be Foo.runtimeType and JRuntimeType. (And maybe we can make the runtime-type hierarchy be the same objects/classes as the static-type hierarchy for all non-generics?)
From discussion offline:
Option 1: list.isIntanceOf(ArrayList.jClass) if we type check against the JClass, because the class handle has generics erased by definition. Possible downside: more handles.
Option 2: Uninstantiated type hierarchy. Unbound type parameters. (Downside: surgery.)
Option 3: The static type hierarchy, but all type arguments must be instantiated to bounds. (ArrayList.type(JString.type) is not allowed, ArrayList.type(JObject.type). We can prevent people having to write having the bounds by adding a ArrayList.rawType.
~The problem with option 3 is that we can't actually code it for a general type class FooType since each type class has an arbitrary number of generic type variables.~ We could also generate a isBaseType method per typeclass that does this for us and call it from isA – but again that makes the codegen more complex than necessary.
Option 1 is already implemented but we also have a isA method that gets a type class instead of a JClass. We must at least put a warning in the doc comments that due to type erasure isA could return false positive results.
Option 2 is just not worth the extra complexity.