kotlinpoet: TypeMirror.asTypeName() returns java.lang.String when receiver type is kotlin.String
Overview
Thx for amazing library!
I encountered a problem that TypeMirror.asTypeName()
returns java.lang.String
when receiver type is kotlin.String
. It seems this is a bug so currently we inserted a workaround below to avoid that.
fun TypeName.correctStringType() =
if (this.toString() == "java.lang.String") ClassName("kotlin", "String") else this
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 13
- Comments: 48 (4 by maintainers)
Commits related to this issue
- 修复Inject的类为泛型类时编译报错的问题,参考https://github.com/square/kotlinpoet/issues/236 — committed to richardwrq/KRouter by deleted user 6 years ago
@richardwrq @tschuchortdev This code is ok also for generic classes like List:
Please ask usage questions on stackoverflow. Given the general availability of metadata support, I’m going to close this issue out.
@JakeWharton Any news on this one?
Thx!
I understood the rationale and let me elaborate the situation I encountered. Think about generating some pieces of code with annotation processor(kapt) from below.
Then we expect such code is generated with KotlinPoet:
But now we’ve got the code like:
Sorry I forgot to mention but our workaround is just for our library, so we don’t have to consider any side effect about it(I didn’t intend to add it to KotlinPoet for sure).
Anyway I realized it’s kind of counter-intuitive because it means KotlinPoet user always take care about replacing java String to kotlin String. I don’t come up with good way to deal with but I just reported as feedback 🙇
Please close this issue if it’s not fit for your plan.
https://github.com/square/kotlinpoet/blob/master/kotlinpoet-metadata/README.md
@StefMa @zigzago
List<List<String>>
is converted tokotlin.collections.List<out java.util.List<java.lang.String>>
at my side (kotlin version: 1.3.50&1.3.60). And I find the reason: the firstList
isParameterizedTypeName
so that it can be handled properly byjavaToKotlinType()
. However, the secondList
is actuallyWildcardTypeName
(note the out) so that its converted type is just guessed byClassName
.Interestingly, if we have a
Map<List<Int>, List<Int>>
, it will be converted tokotlin.collections.Map<kotlin.collections.List<kotlin.Int>, out java.util.List<java.lang.Integer>>>
, which means the firstList
is regarded asParameterizedTypeName
correctly, but at the same time the second isWildcardTypeName
.As a result, we can improve
javaToKotlinType()
forWildcardTypeName
case like this:And now it should work while I didn’t test it thoroughly.
The metadata artifacts are separate and were released in 1.4.0. There are full READMEs in their artifacts on the repo.
It’s important to understand that metadata annotations are only present on classes. Not individual functions, types, properties, parameters, or anywhere else.
In short: cases like
TypeMirror.asTypeName()
orType.asTypeName()
will never work as intended for Kotlin compiler intrinsic types. APIs like this simply cannot look at individual types in isolation and have enough context to understand them. You must have access to the appropriate metadata that describes them in context. There are no silver bullets here, this is how Kotlin works.You can, however, derive the metadata context from navigable elements like methods, fields, etc. You simply traverse up its hierarchy to the first enclosing class, which will have the needed metadata annotation on it. Then you can connect that parsed information back to your source element and connect the dots to deduce the correct type. This is exactly how the snippet I provided above for
overriding
works, and it works with the 1.4.0 metadata release.There is possibly an argument to be made that either kotlinpoet-metadata artifacts should live directly in the main KotlinPoet artifact, or the existing problematic APIs should be deprecated and moved into the kotlinpoet-metadata artifact where they can ask for the needed classpath information. At this point I think that’s a separate issue.
@CasualGitEnjoyer the artifacts have been renamed, which invalidated the old URLs. Here’s an up-to-date link for interop-kotlinx-metadata docs: https://square.github.io/kotlinpoet/interop-kotlinx-metadata/.
Did you look at their docs on the project site?
https://square.github.io/kotlinpoet/kotlinpoet_metadata/
https://square.github.io/kotlinpoet/kotlinpoet_metadata_specs/
For any future reader here is a very interesting talk about metadata @JohnOberhauser https://www.youtube.com/watch?v=uHPti6Z02tI
@ZacSweers are there any examples on how to use that?
Is there a “correct” way to handle this yet?
A map is not the correct nor accurate solution to this problem since it’s not a 1:1 mapping. Multiple distinct Kotlin types are represented as the same JVM type and it’s only exacerbated by inline classes. Parsing the metadata is the only way forward.
Api has changed a bit (use parameterizedBy instead of ParameterizedTypeName.get):
after 1.0.0-RC1 the above answer will need some changes in order to work, these lines to be specific.
====>
this was suggested on #424 .