The idea is to have a class TaxPayer, subclasses Person and Business. We'll also have a TaxStrategy, and subclasses PersonalTaxStrategy and BusinessTaxStrategy. Each TaxPayer knows its strategy, and this has to be done in a type-safe way.
I want to extend that example with a 'universal' tax, that may be applied to any TaxPayer.
Without generics (imports, package declarations omitted for brevity; calculations and test assertions are meaningless):
The 'best' I've been able to come up using generics is:
Now, this means using raw types for UniversalTaxStrategy. Can you tell me how to do this better?
I don't really see why you would want to use generics. The problem you're trying to solve can be solved with just polymorphism.
By the way this:
is the same as:
"Any fool can write code that a computer can understand. Good programmers write code that humans can understand." --- Martin Fowler
Please correct my English.
Joined: May 06, 2010
Suppose there are a number of different tax strategies for Person, and a number of different tax strategies for Business (and possibly other subtypes of TaxPayer) - I only showed 1 for each for sake of simplicity. I want to make sure that when instantiating a Person, I can only assign tax strategies compatible with its type.
Now suppose there's a TaxAgency, it has a List<TaxPayer> taxPayers, and wants to collect taxes:
Now, of course, I could modify TaxPayer not to include the TaxStrategy, and implement getTax() in each TaxPayer subclass independently, but that is redundant.
I know that the cast is not needed here:
I told you tax calculation was just a dummy - the cast was just included to hint at real behaviour, invocation of Business-specific methods, and to demonstrate the problems that could happen at run-time, were the wrong strategy assigned to the TaxPayer subclass instance. The real calculation could be like
Similarly for Person, the calculation would involve gathering Person-specific methods, so the casts are needed (in the non-generic case).
You could then say, OK, but I'll be clever, and write Business like this:
And then I'd ask:
OK, now how do you implement UniversalTaxStrategy in a way that it's applicable to Business and to Person, too?
And then you'd say:
Simple, I'd create an interface for each (TaxStrategyForBusiness, TaxStrategyForPerson), and have UniversalTaxStrategy implement each; and I'd change Business to use TaxStrategyForBusiness instead of BusinessTaxStrategy, and I'd use TaxStrategyForPerson in Person.
And then I'd say: you'll end up with lots of code duplication - the private field in each subclass of TaxPayer, the getter method (sure, you could have a protected field of type TaxStrategy, and use that in TaxPayer), plus for each subtype of TaxPayer, you'd need an interface that goes with it, and each time you add a new kind of TaxPayer, you'd need to add the corresponding interface to the implements clause of UniversalTaxStrategy...
In which you only want to allow the UniversalTaxStrategy and BusinessStrategies for business but not any CitizenStrategies. I don't believe that the compiler can enforce that because a CitizenStrategy IS-A UniversalTaxStrategy and you want to allow that. It is going against the principle of inheritance. It can be done at runtime by checking if the strategy is a UniversalTaxStrategy or BusinessStrategy.
Joined: May 06, 2010
No, I'd like something like this:
With generics, the way I defined TaxPayer<P extents TaxPayer<P>> allows it to use any TaxStrategy<? super P>, that is, any TaxStrategy applicable to the specific TaxPayer subclass P, or one of its (TaxPayer or more specific) ancestors.
Ideally, I'd like to have something like UniversalTaxStrategy implements TaxStrategy<TaxPayer<?>>, but that won't compile.
The Java Generics and Collections book does have DodgingTaxStrategy:
That can be applied to TaxPayer's subclasses, but only by creating several, subtype-specific instances, e.g. you'd have:
I'd like to know if it's possible to do something like that (UniversalTaxStrategy) that does not require a type parameter (and if it can be done, I'd like to know how ). It works with the raw type, as demonstrated, but of course that results in warnings, and use of raw types.
Or you could make something that delegates the calls to the UniversalStrategy:
That way you can define Business to only accept a BusinessTaxStrategy and Citizen to only accept CitizenTaxStrategy and keep the code in one place.
Joined: May 06, 2010
I don't like the last solution. The only thing we reuse from BusinessTaxStrategy is its 'BusinessTaxStrategy-ness'. Using that approach, it'd probably be better to refactor the code, making BusinessTaxStrategy an interface, and implementing it.
ps. "implements TaxStrategy" is redundant if we leave the code as it is, since we already have class BusinessTaxStrategy implements TaxStrategy.
Now, let's steer away from trying to solve this without generics (not because it's wrong, but because it's not the thing at the heart of my question). What I'm interested in is if it's possible to create an unparameterised UniversalBusinessStrategy type and have no warnings (without @SuppressWarnings, of course).