commercetools/commercetools-sdk-java-v2

Make constructors of default implementations of the API interfaces public

Akii opened this issue · 3 comments

Akii commented

Is your feature request related to a problem? Please describe.
Currently, all *Impl classes like AddressImpl are created with a public empty constructor, and a package-private constructor that has all class properties as parameter. When extending, I cannot call this constructor. Instead I need to call the setters. This is problematic when fields are added because the compiler cannot help me identifying missing fields. I currently also do not understand why this constructor is package-private in the first place.

Describe the solution you'd like
Make all constructors of the default implementation of API interfaces public.

Describe alternatives you've considered

  • Calling setters and just hope I catch all changes.
  • Not extending the class but implement the interface otherwise. This introduces a bit of complexity in code generation but would actually be safer. If the proposed solution is not possible, I will fall back to that alternative, which is also ok.

Additional context
It's for the library, I want as much help from the compiler as possible :)

As the constructor with parameters is package private we can ensure that adding fields or even reordering them is not a breaking change e.g. reorder fields of the customer class.

If there is a need to extend it should be done either by implementing the interface or another possibility could be to decorating the Impl class.

Akii commented

Good point. I will just go ahead and implement the interface again instead of using inheritance. Thank you!

Akii commented

Your comment made me think through Kotlin delegation again.. and what can I say, it's perfect.

package test.`package`.address

import com.commercetools.api.models.common.Address
import io.vrap.rmf.base.client.utils.Generated
import test.`package`.custom_fields.AddressCustomFieldsType

@Generated
public class TypedAddress(
  address: Address,
  private val custom: AddressCustomFieldsType?
) : Address by address {
  public override fun getCustom(): AddressCustomFieldsType? = this.custom
}

Now all that is left is some minor JSON transformation. It's a bit more tricky for the product types but worth it considering the code generation will be more resilient to changes of the API while maintaining absolute compatibility.

Thanks again.