Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to omit modifier for protected function in object? #1389

Closed
toddobryan opened this issue Oct 17, 2022 · 5 comments
Closed

How to omit modifier for protected function in object? #1389

toddobryan opened this issue Oct 17, 2022 · 5 comments
Labels

Comments

@toddobryan
Copy link

If I have

interface PublicInterface {
    val myInt: Int 
}

abstract class Foo {
    protected inner class Bar : PublicInterface {
        override val myInt: Int = 42
    }
    
    protected abstract fun Bar.toInt(): Int
}

object FooInstance : Foo() {
    protected override fun Bar.toInt(): Int = myInt
}

I get an error saying that Modifier 'protected' is not applicable inside 'standalone object'. So I need to remove the protected modifier.

Note that I can't replace protected with public as that will trigger a public member exposes its protected (in Foo) receiver type Bar.

So, how do I generate this with Kotlinpoet? I can't figure out how to create the method with just the override modifier and nothing else, but that's the only way to avoid an error.

@JakeWharton
Copy link
Collaborator

There is no way to generate this with KotlinPoet at present.

@JakeWharton
Copy link
Collaborator

Test is here: https://kotlin.godbolt.org/z/vch44E1qK

Note that explicit API mode is ON. Adding any visibility modifier to the override function will cause compilation failure.

@JakeWharton
Copy link
Collaborator

Somewhat related to #1301 in that there are situations in which we can omit explicit visibility modifiers. In this case, omission is actually critical.

@toddobryan
Copy link
Author

toddobryan commented Oct 17, 2022

Any possible workaround anyone can think of? Is there any way in Kotlinpoet to just output text (knowing you'd be responsible for imports and such) as a way to work around it when the really structured APIs make life difficult?

(An example is that Bar receiver above. It's defined in the scope where it's used, but Kotlinpoet makes me give the full ClassName, so I end up with Foo.Bar.toInt() when it would be much nicer to just have Bar.toInt().)

@Egorand
Copy link
Collaborator

Egorand commented May 24, 2023

Looks like #1550 made generating this code possible!

@Test fun protectedOverrideInObject() {
  val publicInterface = TypeSpec.interfaceBuilder("PublicInterface")
    .addProperty("myInt", Int::class)
    .build()
  val abstractFoo = TypeSpec.classBuilder("Foo")
    .addModifiers(KModifier.ABSTRACT)
    .addType(TypeSpec.classBuilder("Bar")
      .addModifiers(KModifier.PROTECTED, KModifier.INNER)
      .addSuperinterface(ClassName("", "PublicInterface"))
      .addProperty(PropertySpec.builder("myInt", Int::class)
        .addModifiers(KModifier.OVERRIDE)
        .initializer("42")
        .build())
      .build())
    .addFunction(FunSpec.builder("toInt")
      .receiver(ClassName("", "Bar"))
      .addModifiers(KModifier.PROTECTED, KModifier.ABSTRACT)
      .returns(Int::class)
      .build())
    .build()
  val fooInstance = TypeSpec.objectBuilder("FooInstance")
    .superclass(ClassName("", "Foo"))
    .addFunction(FunSpec.builder("toInt")
      .addModifiers(KModifier.OVERRIDE)
      .receiver(ClassName("", "Bar"))
      .returns(Int::class)
      .addStatement("return myInt")
      .build())
    .build()
  assertThat(FileSpec.builder("test", "Test")
    .addType(publicInterface)
    .addType(abstractFoo)
    .addType(fooInstance)
    .build()
    .toString())
    .isEqualTo("""
      |package test
      |
      |import Bar
      |import Foo
      |import PublicInterface
      |import kotlin.Int
      |
      |public interface PublicInterface {
      |  public val myInt: Int
      |}
      |
      |public abstract class Foo {
      |  protected abstract fun Bar.toInt(): Int
      |
      |  protected inner class Bar : PublicInterface {
      |    override val myInt: Int = 42
      |  }
      |}
      |
      |public object FooInstance : Foo() {
      |  override fun Bar.toInt(): Int = myInt
      |}
      |""".trimMargin())
}

@Egorand Egorand closed this as completed May 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants