Understanding Kotlin Generic Bounds and Enum Classes vs String Resources

Generics enable classes and functions to work with multiple data types while maintaining type safety. They provide better code reusability and type checking compared to simple inheritance hierarchies.

Upper Bounds

An upper bound restricts a type paramter to be a specific type or its subtypes. In Kotlin, use the colon syntax to specify an upper bound:

fun <T : Animal> processPet(pet: T) {
    // T is guaranteed to be Animal or a subclass
    pet.eat()
}

In Java, the equivalent syntax uses the extends keyword:

public <T extends Animal> void processPet(T pet) {
    pet.eat();
}

Lower Bounds

Lower bounds restrict a type to be a supertype of the specified class. Kotlin uses the in keyword for this purpose:

fun <T in Animal> addToCollection(item: T) {
    // item can be Animal or any supertype
}

Java uses the ? super T syntax for lower bounds.

Multiple Bounds

Kotlin supports multiple bounds using the where clause, allowing a type parameter to satisfy multiple constraints:

fun <T> executeAction(item: T) where T : Runnable, T : Serializable {
    item.run()  // From Runnable
    // item can also be serialized
}

Java uses the & operator to combine multiple bounds:

public <T extends Runnable & Serializable> void executeAction(T item) {
    item.run();
}

Enum Classes in Kotlin

Enum classes provide a type-safe way to represent a fixed set of related values. They are particularly useful when you need to ensure values are validated at compile time rather than runtime.

Consider a simple enum representing order states:

enum class OrderStatus {
    Processing,
    Shipped,
    Delivered
}

fun processOrder(status: OrderStatus) {
    when (status) {
        OrderStatus.Processing -> handleProcessingOrder()
        OrderStatus.Shipped -> handleShippedOrder()
        OrderStatus.Delivered -> handleDeliveredOrder()
    }
}

The compiler will warn you if you miss handling any enum case in a when expression, preventing runtime errors from unhandled states.

Enums with Properties

Enums can include properties and methods, making them suitable for binding to UI resources:

enum class UserRole(val resourceId: Int) {
    Administrator(R.string.role_admin),
    StandardUser(R.string.role_user),
    Guest(R.string.role_guest)
}

fun displayRoleLabel(context: Context, role: UserRole): String {
    return context.getString(role.resourceId)
}

Enum Classes vs String Resources

When deciding between enum classes and string resources, consider the following distinctions:

Use Enum Classes When:

  • Values represent discrete states or categories in your domain logic
  • Type safety is required to prevent invalid values
  • You need to add behavior or properties associated with each value
  • The compiler should enforce handling of all posible cases

Use String Resources When:

  • Text is purely for display purposes and UI localization
  • Values may change without code deployment
  • Non-technical users need to modify text content
// Domain logic using enums for type safety
enum class PaymentMethod {
    CreditCard,
    PayPal,
    BankTransfer
}

fun processPayment(method: PaymentMethod) {
    when (method) {
        PaymentMethod.CreditCard -> handleCardPayment()
        PaymentMethod.PayPal -> handlePayPalPayment()
        PaymentMethod.BankTransfer -> handleBankTransfer()
    }
}

// UI display using string resources
fun getPaymentDisplayText(context: Context, method: PaymentMethod): String {
    val stringRes = when (method) {
        PaymentMethod.CreditCard -> R.string.payment_credit_card
        PaymentMethod.PayPal -> R.string.payment_paypal
        PaymentMethod.BankTransfer -> R.string.payment_bank_transfer
    }
    return context.getString(stringRes)
}

This separation keeps domain logic type-safe while allowing UI text to be localized and updated independently.

Tags: kotlin generics type-bounds Enums Android

Posted on Fri, 05 Jun 2026 17:21:21 +0000 by rajah