kotlinpoet icon indicating copy to clipboard operation
kotlinpoet copied to clipboard

Properties with inferred types

Open Fuyukai opened this issue 5 years ago • 10 comments

It would be nice to be able to create PropertySpecs that do not have types declared and are auto-inferred by Kotlin, e.g. val someProperty = initialiser over val someProperty: <type> = initialiser.

Right now, PropertySpec.Builder requires a Type of some sort and PropertySpec.emit() also always emits the type info.

Fuyukai avatar Dec 31 '19 16:12 Fuyukai

What's the use case?

JakeWharton avatar Dec 31 '19 16:12 JakeWharton

Extension properties for a Java class. I have to manually convert Java->Kotlin types for the property type when the compiler can do it for me (and way better then I could, too).

Fuyukai avatar Dec 31 '19 17:12 Fuyukai

I think this is a legit use case, inferring correct types is not trivial. We should look into making the types optional where possible.

Egorand avatar Dec 31 '19 17:12 Egorand

As we've decided to generate library mode-compliant code by default, and this requires that types for public members are declared explicitly, this feature will have to be limited to non-public properties. Does this change things? Should we still keep this issue in the backlog, or close it?

Egorand avatar Jun 29 '20 16:06 Egorand

For the usage case I had back when I opened this, that would be fundamentally useless.

Fuyukai avatar Jun 30 '20 17:06 Fuyukai

I have a similar case:

private val something: Map<String, Class<*>> = mapOf(/**/)

How to generate code like this, it's difficult to declare the Class<*>

avenwu avatar Jan 13 '21 11:01 avenwu

@JakeWharton @Egorand Any suggestion?

avenwu avatar Jan 13 '21 11:01 avenwu

TypeName.STAR will give you the *. Then just parameterize a TypeName which represents Class using it.

JakeWharton avatar Jan 13 '21 15:01 JakeWharton

cool,thanks

avenwu avatar Jan 13 '21 23:01 avenwu

Works like a charm 👍

Map::class.asClassName().parameterizedBy(
    String::class.asTypeName(),
    Class::class.asTypeName().parameterizedBy(STAR)
)

Output

Map<String, Class<*>>

avenwu avatar Jan 14 '21 03:01 avenwu

has anyone fixed this!!!!

YudingZhou avatar Sep 22 '22 01:09 YudingZhou

No. And without a compelling case for it we have no plans to. Sorry.

JakeWharton avatar Sep 22 '22 04:09 JakeWharton

I would like to generate this: companion object { val postgreSQLContainer = PostgreSQLContainer("postgres:15.3") } That's valid Kotlin code and is running just fine (and avoiding issues with this container classes), so why not to support it?

tleipzig avatar Jun 09 '23 12:06 tleipzig

Can you explain why you are unable to know the type of the expression?

The library does not endeavor to support 100% of all possible syntax forms so if you know the type then we don't actually need to support implicit types.

JakeWharton avatar Jun 09 '23 12:06 JakeWharton

It this particular case you need to add a custom type first and cast it, something like val postgreSQLContainer:PostgreSQLContainerType = (PostgreSQLContainerType)PostgreSQLContainer("postgres:15.3") That's a very ugly workaround and can easily be avoided by removing the type. Also I think many Kotlin developers like to avoid the types when they are not needed, as this makes the code shorter. But up to you I guess if you want to support this. Having a builder without a type would be enough.

tleipzig avatar Jun 09 '23 12:06 tleipzig

If you generate val postgreSQLContainer = PostgreSQLContainer("postgres:15.3") the inferred type will be PostgreSQLContainer though, right? Not sure I understand how being able to avoid explicit types helps improve val postgreSQLContainer:PostgreSQLContainerType = (PostgreSQLContainerType)PostgreSQLContainer("postgres:15.3").

Egorand avatar Jun 09 '23 14:06 Egorand

Moreover, if there's a need to cast it means you're actually changing the type which would otherwise be inferred to something else.

JakeWharton avatar Jun 09 '23 14:06 JakeWharton

That's a specific Kotlin issue with recursive generic types, see here https://stackoverflow.com/questions/59007414/testcontainers-postgresqlcontainer-with-kotlin-unit-test-not-enough-informatioThere are many workarounds, but just leaving out the type is working for me (and providing it will create errors). -------- Original message --------From: Egor Andreevich @.> Date: 6/9/23 18:22 (GMT+01:00) To: square/kotlinpoet @.> Cc: tleipzig @.>, Comment @.> Subject: Re: [square/kotlinpoet] Properties with inferred types (#858) If you generate val postgreSQLContainer = PostgreSQLContainer("postgres:15.3") the inferred type will be PostgreSQLContainer though, right? Not sure I understand how being able to avoid explicit types helps improve val postgreSQLContainer:PostgreSQLContainerType = (PostgreSQLContainerType)PostgreSQLContainer("postgres:15.3").

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

tleipzig avatar Jun 09 '23 16:06 tleipzig

Right so I have used this library before and recall the problem. It uses a recursive generic which is something you really only want to do on an abstract base class but they use it on a non-abstract class that is instantiable. These types are non-denotable when used directly in both Java and Kotlin. I think the standard practice in Java is to use a raw type, but obviously Kotlin does not have raw types. The workarounds are to subclass where the type parameter can become your subtype, or to do all configuration of the type in an apply block and have the parameter type use a wildcard and optionally be a supertype like JdbcDatabaseContainer.

In any case, while I understand it's annoying, I don't think is enough to warrant adding what is otherwise a somewhat dangerous feature to the library. It also would break the ability to use the explicit API mode which currently all code generated by the library will support by default.

JakeWharton avatar Jun 09 '23 19:06 JakeWharton

Thank you very much for your understanding and looking into this! This is quite an edge case of cause, and if the implications of a change are so big, it's probably better to keep the current state. -------- Original message --------From: Jake Wharton @.> Date: 6/9/23 21:23 (GMT+01:00) To: square/kotlinpoet @.> Cc: tleipzig @.>, Comment @.> Subject: Re: [square/kotlinpoet] Properties with inferred types (#858) Right so I have used this library before and recall the problem. It uses a recursive generic which is something you really only want to do on an abstract base class but they use it on a non-abstract class that is instantiable. These types are non-denotable when used directly in both Java and Kotlin. I think the standard practice in Java is to use a raw type, but obviously Kotlin does not have raw types. The workarounds are to subclass where the type parameter can become your subtype, or to do all configuration of the type in an apply block and have the parameter type use a wildcard and optionally be a supertype like JdbcDatabaseContainer. In any case, while I understand it's annoying, I don't think is enough to warrant adding what is otherwise a somewhat dangerous feature to the library. It also would break the ability to use the explicit API mode which currently all code generated by the library will support by default.

—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you commented.Message ID: @.***>

tleipzig avatar Jun 09 '23 20:06 tleipzig