Skip to content

%M for Members

Similar to types, KotlinPoet has a special placeholder for members (functions and properties), which comes handy when your code needs to access top-level members and members declared inside objects. Use %M to reference members, pass an instance of MemberName as the argument for the placeholder, and KotlinPoet will handle imports automatically:

val createTaco = MemberName("com.squareup.tacos", "createTaco")
val isVegan = MemberName("com.squareup.tacos", "isVegan")
val file = FileSpec.builder("com.squareup.example", "TacoTest")
  .addFunction(
    FunSpec.builder("main")
      .addStatement("val taco = %M()", createTaco)
      .addStatement("println(taco.%M)", isVegan)
      .build()
  )
  .build()
println(file)

The code above generates the following file:

package com.squareup.example

import com.squareup.tacos.createTaco
import com.squareup.tacos.isVegan

fun main() {
  val taco = createTaco()
  println(taco.isVegan)
}

As you can see, it’s also possible to use %M to reference extension functions and properties. You just need to make sure the member can be imported without simple name collisions, otherwise importing will fail and the code generator output will not pass compilation. There’s a way to work around such cases though - use FileSpec.addAliasedImport() to create an alias for a clashing MemberName:

val createTaco = MemberName("com.squareup.tacos", "createTaco")
val createCake = MemberName("com.squareup.cakes", "createCake")
val isTacoVegan = MemberName("com.squareup.tacos", "isVegan")
val isCakeVegan = MemberName("com.squareup.cakes", "isVegan")
val file = FileSpec.builder("com.squareup.example", "Test")
  .addAliasedImport(isTacoVegan, "isTacoVegan")
  .addAliasedImport(isCakeVegan, "isCakeVegan")
  .addFunction(
    FunSpec.builder("main")
      .addStatement("val taco = %M()", createTaco)
      .addStatement("val cake = %M()", createCake)
      .addStatement("println(taco.%M)", isTacoVegan)
      .addStatement("println(cake.%M)", isCakeVegan)
      .build()
  )
  .build()
println(file)

KotlinPoet will produce an aliased import for com.squareup.tacos2.isVegan:

package com.squareup.example

import com.squareup.cakes.createCake
import com.squareup.tacos.createTaco
import com.squareup.cakes.isVegan as isCakeVegan
import com.squareup.tacos.isVegan as isTacoVegan

fun main() {
  val taco = createTaco()
  val cake = createCake()
  println(taco.isTacoVegan)
  println(cake.isCakeVegan)
}

MemberName and operators

MemberName also supports operators, you can use MemberName(String, KOperator) or MemberName(ClassName, KOperator) to import and reference operators.

val taco = ClassName("com.squareup.tacos", "Taco")
val meat = ClassName("com.squareup.tacos.ingredient", "Meat")
val iterator = MemberName("com.squareup.tacos.internal", KOperator.ITERATOR)
val minusAssign = MemberName("com.squareup.tacos.internal", KOperator.MINUS_ASSIGN)
val file = FileSpec.builder("com.example", "Test")
  .addFunction(
    FunSpec.builder("makeTacoHealthy")
      .addParameter("taco", taco)
      .beginControlFlow("for (ingredient %M taco)", iterator)
      .addStatement("if (ingredient is %T) taco %M ingredient", meat, minusAssign)
      .endControlFlow()
      .addStatement("return taco")
      .build()
  )
  .build()
println(file)

KotlinPoet will import the extension operator functions and emit the operator.

package com.example

import com.squareup.tacos.Taco
import com.squareup.tacos.ingredient.Meat
import com.squareup.tacos.internal.iterator
import com.squareup.tacos.internal.minusAssign

fun makeTacoHealthy(taco: Taco) {
  for (ingredient in taco) {
    if (ingredient is Meat) taco -= ingredient
  }
  return taco
}