• 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
  • Paul Clapham
  • Tim Cooke
  • Jeanne Boyarsky
  • Liutauras Vilda
Sheriffs:
  • Frank Carver
  • Henry Wong
  • Ron McLeod
Saloon Keepers:
  • Tim Moores
  • Frits Walraven
  • Tim Holloway
  • Stephan van Hulst
  • Carey Brown
Bartenders:
  • Al Hobbs
  • Piet Souris
  • Himai Minh

Best way to "extend" or improve a bunch of objects

 
Rancher
Posts: 4686
7
Mac OS X VI Editor Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
There may be a trivial and clean way to do this, but I'm not seeing it.

Lets suppose you have a nice Person class defined, with lots of the usual fields (name, email, age, gender, etc). With all sorts of code that works with it, stores and fetches instances to the RDBMS, and similar non-trivial stuff already implemented.

Now you want to have a subclass, say TalentedPerson that is a subclass of Person. Each TalentedPerson has a talent, say writing Java, playing guitar, etc.

You have code that used to work great with the Person class, and now you want to make it work with TalentedPerson class. The key is that all old code that works with Person needs to work with TalentedPersons. Only new code that is designed for TalentedPerson instances cares about functions like "getTalent()"

What is the best constructor for the TalentedPerson? Should it take a Person instance and remember it? Or do we want a factory that accepts a
List<Person> as input and returns a List<TalentedPerson>?

Thanks

 
author and iconoclast
Posts: 24204
44
Mac OS X Eclipse IDE Chrome
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Pat,

Well, to first order, you create TalentedPerson as a subclass of Person, and then any function that took a Person or List<Person> as an argument will also accept TalentedPerson or List<TalentedPerson>. There you go, all done.

Except, as you hint, there's the constructor issue. If that assembled mass of code uses Person's constructor a lot, then there are a lot of Person objects in the system, and you'd rather suddenly start having all TalentedPerson objects.

The solution to this is a factory method. If every instance of

new Person(arg1, arg2)

could be replaced with

PersonFactory.newPerson(arg1, arg2)

then you could just modify newPerson(arg1, arg2) to return a TalentedPerson with a default talent. And you could add

PersonFactory.newPerson(arg1, arg2, theTalent)

for when the talent is known.

Changing that big mass of code in this way is pretty easy with a decent IDE; smart search-and-replace along with, possibly, fixing up some import statements. That's it. You can then make Person's constructors package-private, so nobody will use them accidentally.
 
Pat Farrell
Rancher
Posts: 4686
7
Mac OS X VI Editor Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Yeah, I was doing the subclass thing:



It was the factory thing that was unclear.

Naturally, in the real world, you only want to use TalentedPerson sometimes.
 
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Ernest Friedman-Hill wrote:Hi Pat,

Well, to first order, you create TalentedPerson as a subclass of Person, and then any function that took a Person or List<Person> as an argument will also accept TalentedPerson or List<TalentedPerson>. There you go, all done.



To be clear, a method that takes a List<Person> argument will not accept a List<TalentedPerson>. Generic type parameters aren't covariant, a List<TalentedPerson> is not a subclass of List<Person>. For the method to accept both types of Lists, it must be declared to take a List<? extends Person>.
 
Pat Farrell
Rancher
Posts: 4686
7
Mac OS X VI Editor Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Garrett Rowe wrote:To be clear, a method that takes a List<Person> argument will not accept a List<TalentedPerson>. Generic type parameters aren't covariant, a List<TalentedPerson> is not a subclass of List<Person>. For the method to accept both types of Lists, it must be declared to take a List<? extends Person>.



You sure about this?

A TalentedPerson isa Person. So a List<TalentedPerson> is a list of Person objects.

You don't get the automatic type casts without your List<? extends Person>, but otherwise, its working fine for me
 
Marshal
Posts: 76393
364
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Pat Farrell wrote:

Garrett Rowe wrote: . . . Generic type parameters aren't covariant, . . .

You sure about this?

Yes, that is correct. There are details in the Java™ Tutorials, where there is a section about subtyping.
 
Pat Farrell
Rancher
Posts: 4686
7
Mac OS X VI Editor Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:Yes, that is correct. There are details in the Java™ Tutorials, where there is a section about subtyping.


I found that tutorial section to be misleading. While that is technically correct in a single compilation unit, its not true in general. Its one of the curses with Java's generic design, or how they hacked generics into Java as an afterthought.

If you call a factory (or other hidden thing) that returns a List<Person> that just happens to really be populated with a bunch of TalentedPerson objects, then it all works the way you would intuitative expect.

The driving point to my original post was to not have to change tons of code that uses Person, or List<Person>

javac is pretty dumb, and all the generic mangling is lost in the emmitted bytecode
 
Garrett Rowe
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I only wanted to clarify that in Java a method that expects a List<Person> will not accept a List<TalentedPerson>. This may not be what your original post was about, but it was implied by Ernest's reply.


If you uncomment line 43 the given code will not compile. It will compile if you change the signature of printNames to:

public void printNames(List<? extends Person> people)

 
Pat Farrell
Rancher
Posts: 4686
7
Mac OS X VI Editor Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Garrett Rowe wrote:I only wanted to clarify that in Java a method that expects a List<Person> will not accept a List<TalentedPerson>. This may not be what your original post was about, but it was implied by Ernest's reply.



Er, but it will, if the compiler doesn't see the call. Really, it works. If you have a factory that returns a List<Person> that happens to be populated as a set of
List<TalentedPerson> then the call to other functions that expect a List<Person> is happy and it all works.

I understand that you are pointing out that the compiler won't do it if it knows about it. But in fact, what the compiler doesn't know won't hurt it.
 
Garrett Rowe
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Could you demonstrate what you're trying to explain with some code, because I'm not getting it at all.
 
Pat Farrell
Rancher
Posts: 4686
7
Mac OS X VI Editor Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I think you are over thinking this. its really simple. Just have code such as



And you have a Person object that happens to be a TalentedPerson, you can then trivially add() them to any list.
 
Garrett Rowe
Ranch Hand
Posts: 1296
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ahhh... That's exactly not what i was talking about. I was talking about a List<TalentedPerson>, not a List<Person> that happened to be filled with TalentedPerson instances. A List<TalentedPerson> is not a subtype of a List<Person>, and cannot be used where a List<Person> is expected. That's all I was trying to convey.
 
Pat Farrell
Rancher
Posts: 4686
7
Mac OS X VI Editor Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Garrett Rowe wrote:That's exactly not what i was talking about. I was talking about a List<TalentedPerson>, not a List<Person> that happened to be filled with TalentedPerson instances. A List<TalentedPerson> is not a subtype of a List<Person>



For sure. A list of apples and a list of oranges are two lists, when you merge them, you end up with a list of fruit.

 
crispy bacon. crispy tiny ad:
the value of filler advertising in 2021
https://coderanch.com/t/730886/filler-advertising
reply
    Bookmark Topic Watch Topic
  • New Topic