Reputation: 5141
I thought I had a decent understanding of Kotlin's in
, out
and *
modifiers but I cannot rewrite one class from Java to Kotlin. Maybe someone could help me out. In my app I'm using retrofit and in all of my projects I have this ApiResponseValidatorJava class which is responsible for validating API response models such as Customer. How would this class look written in Kotlin?
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
public class ApiResponseValidatorJava extends Converter.Factory {
private HashMap<Type, ModelValidator> validators = new HashMap();
public ApiResponseValidatorJava(ModelValidator... validators) {
for (ModelValidator v : validators) {
this.validators.put(v.getModelType(), v);
}
}
@Override
public Converter<ResponseBody, ?> responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, Object> mainConverter = retrofit.nextResponseBodyConverter(this, type, annotations);
return new Converter<ResponseBody, Object>() {
@Override
public Object convert(ResponseBody value) throws IOException {
Object apiResponseModel = mainConverter.convert(value);
if (validators.containsKey(type)) {
validators.get(type).validate(apiResponseModel);
}
return apiResponseModel;
}
};
}
}
import java.lang.reflect.Type
interface ModelValidator<T> {
fun getModelType(): Type
fun validate(data: T)
}
import retrofit2.Call
import retrofit2.http.*
interface Api {
@GET("api/session/customer")
fun getCustomer(): Call<Customer>
}
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class Customer(
@Json(name = "id")
val id: String,
@Json(name = "phone")
val phone: String
)
class CustomerResponseValidator : ModelValidator<Customer> {
override fun getModelType() = Customer::class.java
override fun validate(customer: Customer) {
if (!customer.phone.startsWith("+212")) {
throw IllegalAccessException("Phone doesn't start with +212")
}
}
}
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
private fun buildRetrofit(): Api {
val apiResponseValidator = ApiResponseValidatorJava(
CustomerResponseValidator()
)
return Retrofit.Builder()
.baseUrl("https://example.com/")
.client(OkHttpClient.Builder().build())
.addConverterFactory(apiResponseValidator)
.addConverterFactory(MoshiConverterFactory.create())
.build()
.create(Api::class.java)
}
}
// gradle dependecies
implementation group: 'com.squareup.retrofit2', name: 'retrofit', version: '2.6.2'
implementation group: 'com.squareup.retrofit2', name: 'converter-moshi', version: '2.6.2'
implementation group: 'com.squareup.moshi', name: 'moshi', version: '1.8.0'
kapt "com.squareup.moshi:moshi-kotlin-codegen:1.8.0"
Upvotes: 0
Views: 102
Reputation: 16224
Since you want to receive as input a ModelValidator
with every generic type, you can use ModelValidator<out Any?>
.
You can use something like this:
class ApiResponseValidatorJava(vararg validators: ModelValidator<out Any?>) : Converter.Factory() {
private val validators = mutableMapOf<Type, ModelValidator<Any?>>().apply {
validators.forEach {
@Suppress("UNCHECKED_CAST")
this[it.getModelType()] = it as ModelValidator<Any?>
}
}
override fun responseBodyConverter(
type: Type,
annotations: Array<Annotation>,
retrofit: Retrofit
): Converter<ResponseBody, *> {
val mainConverter = retrofit.nextResponseBodyConverter<Any?>(this, type, annotations)
return Converter<ResponseBody, Any?> { value ->
mainConverter.convert(value).also { apiResponseModel ->
validators[type]?.validate(apiResponseModel)
}
}
}
}
Upvotes: 1