• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Generics - how to create a universal strategy?

 
Ranch Hand
Posts: 135
4
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
 
Bartender
Posts: 2700
IntelliJ IDE Opera
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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:
 
Istvan Kovacs
Ranch Hand
Posts: 135
4
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Bartender
Posts: 2700
IntelliJ IDE Opera
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Oke but then why do you want to use generics? This problem can be solved with inheritance and polymorphism.
 
Istvan Kovacs
Ranch Hand
Posts: 135
4
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Bartender
Posts: 2700
IntelliJ IDE Opera
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I would do something like this (note not tested nor compiled):
 
Istvan Kovacs
Ranch Hand
Posts: 135
4
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator


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
Bartender
Posts: 2700
IntelliJ IDE Opera
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
But that is easy to implement:
 
Istvan Kovacs
Ranch Hand
Posts: 135
4
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Bartender
Posts: 2700
IntelliJ IDE Opera
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 135
4
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Bartender
Posts: 2700
IntelliJ IDE Opera
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Posts: 135
4
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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
Bartender
Posts: 2700
IntelliJ IDE Opera
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I don't like it either but it will make the compiler enforce that it is a BusinessThingie. Regarding the interface: true.
 
reply
    Bookmark Topic Watch Topic
  • New Topic