Here is how you can do that:
object Main {
// concrete class for all "enum" values (such as OfferType.ADDON or OfferType.SALE)
// that get passed to `getOffer` as second param
class OfferType<T> {
companion object Types {
// "enum" values. Unfortunately Kotlin doesn't support
// type parameters for real enums
@JvmStatic
val ADDON = OfferType<Addon>()
@JvmStatic
val SALE = OfferType<Sale>()
data class Addon(val name: String)
data class Sale(val name: String)
}
}
data class OfferRepresentation(
val addon: OfferType.Types.Addon,
val sale: OfferType.Types.Sale
)
@JvmStatic
fun main(args: Array<String>) {
fun <T> getOffer(offers: OfferRepresentation, type: OfferType<T>): T {
@Suppress("UNCHECKED_CAST")
return when (type) {
OfferType.ADDON -> offers.addon as T
OfferType.SALE -> offers.sale as T
else -> throw IllegalArgumentException("Unsupported type $type")
}
}
val offer = OfferRepresentation(
OfferType.Types.Addon("addon"),
OfferType.Types.Sale("sale")
)
// types are specified explicitly for the sake of demonstration
val addon: OfferType.Types.Addon = getOffer(offer, OfferType.ADDON)
val sale: OfferType.Types.Sale = getOffer(offer, OfferType.SALE)
println("$addon, $sale")
}
}
Note that there is an UNCHECKED_CAST in getOffer function. It's your responsibility to ensure that OfferType.ADDON (OfferType.SALE, etc) values are parameterized with the correct OfferRepresentation's field type and when logic is also correct, otherwise you'll get ClassCast exception in runtime.
UPD. There is alternative implementation that uses a bit of kotlin magic, specifically, reified types. With this approach instead of having separate entities to define the field that you want to be returned from getOffer function you just specify explicit type parameter. Alternatively you can rely on implicit type inference:
object Main2 {
object OfferType {
data class Addon(val name: String)
data class Sale(val name: String)
}
data class OfferRepresentation(
val addon: OfferType.Addon,
val sale: OfferType.Sale
)
//reified type parameters require function to be inline
inline fun <reified T> getOffer(offers: OfferRepresentation): T {
@Suppress("UNCHECKED_CAST")
return when (T::class) {
OfferType.Addon::class -> offers.addon as T
OfferType.Sale::class -> offers.sale as T
else -> throw IllegalArgumentException("Unsupported type ${T::class}")
}
}
@JvmStatic
fun main(args: Array<String>) {
val offer = OfferRepresentation(
OfferType.Addon("addon"),
OfferType.Sale("sale")
)
// types needed to be specified explicitly here
val addon: OfferType.Addon = getOffer(offer)
val sale: OfferType.Sale = getOffer(offer)
// alternatively `getOffer` should be invoked like this:
getOffer<OfferType.Addon>(offer)
println("$addon, $sale")
}
}