mtedone/podam

fails to generate collection for kotlin pojo with default parameters

Closed this issue · 6 comments

Example:

data class Pojo(
    val listValue: List<String>,
    val intValue: Int = 0
)

class PojoTest {
    var factory = PodamFactoryImpl()

    @Test
    fun pojo2() {
        val pojo = factory.manufacturePojoWithFullData(Pojo::class.java)
        Assert.assertEquals(String::class.java, pojo.listValue[0].javaClass)
    }
}

Kotlin compiler creates new constructor for instantiating objects with default parameters and erases generic info for such constructor so constructor. genericParameterTypes returns only Class and not ParameterizedType.

As work around i used special ordering in DataProviderStrategy that prefers constructors with specified ParameterizedType

Hi,

But if Kotlin erases generic type information, what can Podam do about it?

Thanks, Daniil

As work around i used special ordering in DataProviderStrategy that prefers constructors with specified ParameterizedType

private class FixedDataProviderStrategy(private val strategy: DataProviderStrategy) : DataProviderStrategy by strategy {

    override fun sort(constructors: Array<out Constructor<*>>, order: DataProviderStrategy.Order) {
        strategy.sort(constructors, order)
        // https://github.com/mtedone/podam/issues/296
        Arrays.sort(constructors) { a, b ->
            val aHasTypeInfo = a.genericParameterTypes.find { it is ParameterizedType } != null
            val bHasTypeInfo = b.genericParameterTypes.find { it is ParameterizedType } != null
            bHasTypeInfo.compareTo(aHasTypeInfo)
        }
    }
}

Yes, I understood workaround, but no one should randomly shuffle constructors to get desired result. This is not generic and sustainable approach. The main question here is "genericParameterTypes returns only Class and not ParameterizedType" is this is a bug in Kotlin compiler stripping generics away or it is somewhere else.

Thanks, Daniil

This is what Kotlin compiler generates

public final class Pojo {
  @NotNull
  private final List < String > listValue;
  public Pojo(@NotNull List listValue, int intValue) {
    this.listValue = listValue;
    this.intValue = intValue;
  }
  private final int intValue;
  @NotNull public final List < String > getListValue() {
    return this.listValue;
  }
  public final int getIntValue() {
    return this.intValue;
  }

  @NotNull
  public final List < String > component1() {
    return this.listValue;
  }

  public final int component2() {
    return this.intValue;
  }

  @NotNull
  public final Pojo copy(@NotNull List listValue, int intValue) {
    Intrinsics.checkNotNullParameter(listValue, "listValue");
    return new Pojo(listValue, intValue);
  }

  @NotNull
  public String toString() {
    return "Pojo(listValue=" + this.listValue + ", intValue=" + this.intValue + ')';
  }

  public int hashCode() {
    result = this.listValue.hashCode();
    return result * 31 + Integer.hashCode(this.intValue);
  }

  public boolean equals(@Nullable Object other) {
    if (this == other)
      return true;
    if (!(other instanceof Pojo))
      return false;
    Pojo pojo = (Pojo) other;
    return !Intrinsics.areEqual(this.listValue, pojo.listValue) ? false : (!(this.intValue != pojo.intValue));
  }
}

It was able to keep generics in accessors, but not in the constructor. And there is just one constructor.

Thanks, Daniil

You should report this issue to Kotlin compiler.

Thanks, Daniil