Search code examples
kotlinannotations

Restrict callers of method to a class


In Kotlin, I frequently come across the following pattern:

class DlgManager {
   fun dismiss(dlg: Dialogue) {
      // ...
      dlg.onDismiss()
      // ...
   }
}
class Dialogue {
   // Never call this directly, always go through DlgManager!
   fun onDismiss() {
      // ...
   }
}

I would like to make it impossible for arbitrary callers to call onDismiss except if the caller is DlgManager. Is this possible with an annotation?

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class RestrictCallers(val caller: KClass<out Any>)

class DlgManager {
   fun dismiss(dlg: Dialogue) {
      // ...
      dlg.onDismiss()
      // ..
   }
}
class Dialogue {
   @RestrictCallers(DlgManager::class)
   fun onDismiss() {
      // ...
   }
}

How would such an implementation of @RestrictCallers look like?


Solution

  • To do precisely what you want to do would require going outside normal language rules and writing a compiler plugin.

    Opt-in annotations

    However you can get pretty close using an opt-in annotation if you don't mind annotating acceptable callers as well.

    To do this, define your opt-in annotation:

    @RequiresOptIn(message = "Only to be called by annotated callers")
    @Retention(AnnotationRetention.BINARY)
    annotation class RestrictOnDismissCallers
    

    Annotate onDismiss:

    class Dialogue {
       @RestrictOnDismissCallers
       fun onDismiss() {
          // ...
       }
    }
    

    Then an error will be generated by any function trying to call onDismiss except for those annotated with the opt-in annotation, such as:

    @OptIn(RestrictOnDismissCallers::class)
    fun dismiss(dlg: Dialogue)