0

I have a class Transaction with subclasses Income and Expense which have a property category. Incomes and expenses have distinct categories, i.e. an IncomeCategory and ExpenseCategory. I need a list of both incomes and expenses, so I create a list of Transactions. But I need to also access their category, so Transaction also needs a category property. Since the values a category can take on is finite, enums or sealed classes would be very practical. But I need something where the supertype is interchangeable with the subtype.

Is there a way to specify a general category, so that the Transaction class can accept either of IncomeCategory or ExpenseCategory as a parameter?

P.S.: Sorry for the horrible title, I couldn't think of a better way to describe my problem

1 Answers1

0

Here's how you would do it if Category needs to be a sealed class (e.g., instances of IncomeCategory and ExpenseCategory need to carry state). The top level Transaction class uses a generic type to represent its category, so if you want a list of both types of Transactions, you can declare a List<Transaction<Category>>.

sealed class Category
class IncomeCategory: Category()
class ExpenseCategory: Category()

abstract class Transaction<out T: Category>(val category: T)

class Income(category: IncomeCategory): Transaction<IncomeCategory>(category)
class Expense(category: ExpenseCategory): Transaction<ExpenseCategory>(category)

If you need to be able to change the category instance, then you can't declare the type T to be covariant out at the declaration site, and you would have to declare it covariant where you are referencing the list: List<Transaction<out Category>>. Then you can use var instead of val for category.

If Category can just be an enum, this is simpler because you don't need generics:

enum class Category {
    IncomeCategory,
    ExpenseCategory
}

abstract class Transaction(val category: Category)

class Income(): Transaction(Category.IncomeCategory)
class Expense(): Transaction(Category.ExpenseCategory)
Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Is it also possible to add more category values? E.g. when the user wants to add a custom category (sorry, forgot to mention that in the post). – coppermine Nov 17 '20 at 20:10
  • Yes if by “user” you mean user of the library, and if you make Category abstract instead of sealed. Then the user can create new categories and Transaction types following the pattern in my first example. – Tenfour04 Nov 17 '20 at 20:35
  • and if it was the user of the application? – coppermine Nov 18 '20 at 07:27