aspose file tools*
The moose likes Beginning Java and the fly likes Generics - how to create a universal strategy? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Beginning Java
Bookmark "Generics - how to create a universal strategy?" Watch "Generics - how to create a universal strategy?" New topic
Author

Generics - how to create a universal strategy?

Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100
The example comes from Java Generics and Collections; that, in turn, was mostly based on http://www.javaspecialists.eu/archive/Issue123.html

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?

Thanks in advance,
Kofa
Wouter Oet
Saloon Keeper

Joined: Oct 25, 2008
Posts: 2700

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.
Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100
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).
Wouter Oet
Saloon Keeper

Joined: Oct 25, 2008
Posts: 2700

Oke but then why do you want to use generics? This problem can be solved with inheritance and polymorphism.
Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100
I'm studying generics - the problem is not a real-world issue, it's an issue I'd like to solve because I find it interesting.

Regarding the solution without generics - please show me how... I don't see how it could be done without redundancy, in a type-safe way that does not involve casting.
Wouter Oet
Saloon Keeper

Joined: Oct 25, 2008
Posts: 2700

I would do something like this (note not tested nor compiled):
Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100


So you have hard-coded that Business uses BusinessTaxStrategy. But my requirement is:
Suppose there are a number of different tax strategies for Person, and a number of different tax strategies for Business
.

The strategy to use should be assigned from the outside, using the constructor or a setter.
Wouter Oet
Saloon Keeper

Joined: Oct 25, 2008
Posts: 2700

But that is easy to implement:
Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100
But now I can write:


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...
Wouter Oet
Saloon Keeper

Joined: Oct 25, 2008
Posts: 2700

So basically you want:


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.
Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100
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.
Wouter Oet
Saloon Keeper

Joined: Oct 25, 2008
Posts: 2700

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.
Istvan Kovacs
Ranch Hand

Joined: May 06, 2010
Posts: 100
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).
Wouter Oet
Saloon Keeper

Joined: Oct 25, 2008
Posts: 2700

I don't like it either but it will make the compiler enforce that it is a BusinessThingie. Regarding the interface: true.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Generics - how to create a universal strategy?