• Post Reply Bookmark Topic Watch Topic
  • New Topic

Tough generic problem: factory method that returns a list  RSS feed

 
Swerrgy Smith
Ranch Hand
Posts: 94
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello all,

Suppose that I have a super class Animal.java and sub-classes Dog.java and Cat.java
How can I write a "factory" method that return a list of Dog or Cat based on the parameter without explicit type case.

For example:


And I think of the factoryMethod signature like that:


However, this code doesn't work.

If I use explicit type cast then it work but that's what I want to avoid:

Can anyone help me?

Thanks a lot.
 
Jeanne Boyarsky
author & internet detective
Marshal
Posts: 37507
552
Eclipse IDE Java VI Editor
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This works. It doesn't use the enum, but keeps it to preserve your method signature:

 
Swerrgy Smith
Ranch Hand
Posts: 94
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Jeanne, it works better than my first version but it seems that I still have to to some kind of type cast in the method if I really want to use the list and add some element. Is there anyway to avoid completely the type cast?



Jeanne Boyarsky wrote:This works. It doesn't use the enum, but keeps it to preserve your method signature:

 
Scott Shipp
Ranch Hand
Posts: 223
12
Eclipse IDE IntelliJ IDE Java Scala Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Swerrgy Smith wrote:Thanks Jeanne, it works better than my first version but it seems that I still have to to some kind of type cast in the method if I really want to use the list and add some element. Is there anyway to avoid completely the type cast?



Jeanne Boyarsky wrote:This works. It doesn't use the enum, but keeps it to preserve your method signature:



One way is to leave it without a type parameter...but it compiles with warning: "Note: FactoryMethod1.java uses unchecked or unsafe operations."



 
Campbell Ritchie
Marshal
Posts: 56576
172
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Not convinced, I am afraid.
Those if‑elses about type don't look properly object‑oriented to me. Also returning a raw type looks dubious.
 
Jesper de Jong
Java Cowboy
Sheriff
Posts: 16060
88
Android IntelliJ IDE Java Scala Spring
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You can do it with raw types as Scott shows, but this is a bad idea. Raw types only exist for backward compatibility with very old Java versions, where there were no generics. Don't use raw types in new code.

The problem is that type checking is done at compile time but your code wants to decide at runtime what type it's going to return (based on what value is being passed in when the method is called). So the compiler can't check the types, since it's not known at compile time what the method is going to return.

You either have to resort to a cast, or use reflection.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Swerrgy Smith wrote:Thanks Jeanne, it works better than my first version but it seems that I still have to to some kind of type cast in the method if I really want to use the list and add some element. Is there anyway to avoid completely the type cast?

Simple answer: No - because the statement 'new ArrayList<T>()' is not allowed; so whatever actual type you do create has to be cast to a List<T> in order to satisfy the return type.

Winston

PS: Since you're using an enum, you could put that if...else stuff into a switch statement.
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:Simple answer: No - because the statement 'new ArrayList<T>()' is not allowed...

My apologies. That appears to be wrong (you learn something new every day ).

Indeed, the following code:appears to compile (and run) just fine; so maybe that's an alternative for you.

HIH

Winston
 
Scott Shipp
Ranch Hand
Posts: 223
12
Eclipse IDE IntelliJ IDE Java Scala Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Jesper de Jong wrote:
The problem is that type checking is done at compile time but your code wants to decide at runtime what type it's going to return (based on what value is being passed in when the method is called). So the compiler can't check the types, since it's not known at compile time what the method is going to return.


Expanding the explanation. Hope it helps.

I think the problem might be understood as being that Java does not have covariant generics. Java considers List<Animal> and List<Cat> to be different, unrelated types, even though Animal and Cat themselves have a super-subtype relationship. So you can't expect to do anything with a List<Cat> (or List<Dog>) when a List<Animal> is expected. Can't pass a List<Dog> as a method parameter when a List<Animal> is expected, can't return a List<Cat> from a method with a List<Animal> return type.

And thus you can't do:



nor can you do:



Anyway I don't see how not having to cast to an explicit type would be buying you anything? I think the compiler is forcing this because otherwise you lose type safety. Ultimately this question is asking how to get rid of type safety. For example, what is going to happen when you accidentally do this with the code that has now resulted:

 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!