List interface
I'm trying to improve the Genericity of my code by setting the return type of methods that return classes that implement the List interface to List. In other words, rather than force the class user to work with, let's say, an ArrayList returned by one of my methods, I'd like the user to be able to change the actual List class used if need be. Here's an example of how I'm currently creating a list:
public List <String> getNames() {
ArrayList <String> names = new ArrayList <String> ();
//...
// add elements to list here
// ...
return names;
}
I've looked in the Collections class for an appropriate method that will return a concrete List class but all the methods return immutable Lists. I'm looking for a similar method that returns a mutable List because I need to add elements to the List in the method. Any ideas?
This isn't really a pattern or OO question, is it? Anyway, I don't see what's wrong with your example. ArrayList is a concrete mutable List class, and that's what you're returning. You could change your code to the following.
public List<String> getNames() {
List<String> names = new ArrayList<String>();
//...
// add elements to list here
// ...
return names;
}
So what is the problem with this?
> I've looked in the Collections class for an
> appropriate method that will return a concrete List
> class but all the methods return immutable Lists. I'm
> looking for a similar method that returns a mutable
> List because I need to add elements to the List in
> the method. Any ideas?
you could do this without using the generics especially before java 5; but you would be better off not to do what you want to do here for reasons why java 5 added the genarics.
BillKrieger, the problem with that code is that after the assignment to names, the variable contains a reference to an ArrayList, even though it looks like the variable is still a generic List. So, if a caller tried casting the returned List to a Vector, say, a ClassCastException would be thrown because the type is actually ArrayList.
daFei.., are you saying that I should just return an ArrayList for all of the methods in question and force the user to use that type of List? There's nothing inherently wrong with that approach but I try to be as flexible as possible with my code so that the user isn't tied down to my choices in the moment. Once the public interface is defined and the product is shipped, it will be hard to change the code in the case that a new List class is introduced in the future that offers a performance boost. Just a thought.
> daFei.., are you saying that I should just return an
> ArrayList for all of the methods in question and
> force the user to use that type of List? There's
> nothing inherently wrong with that approach but I try
> to be as flexible as possible with my code so that
> the user isn't tied down to my choices in the moment.
> Once the public interface is defined and the product
> is shipped, it will be hard to change the code in
> the case that a new List class is introduced in the
> future that offers a performance boost. Just a
> thought.
you can implemet the interface on your newer and better class, and it will continue to work:)
or as a sub type of the trapped class.
> BillKrieger, the problem with that code is that after
> the assignment to names, the variable contains a
> reference to an ArrayList, even though it looks like
> the variable is still a generic List. So, if a
> caller tried casting the returned List to a Vector,
> say, a ClassCastException would be thrown because the
> type is actually ArrayList.
You can and should declare your method to return List in most cases. That keeps the caller from knowing uimportant implementation details. However, you will have to create a concrete List to return--ArrayList, LinkedList, Vector, etc. You can't cast something to what it's not, and your caller should not be trying to cast a returned List to any other subtype, unless he has done something to ensure it will be that type (like when you pass an array of the type you want returned to List.toArray).
If you don't have a reason to return a specific type of list, then just declare that you return List. If the caller wants something more specific, he'll have to create a new ArrayList or whatever from your results.
Also, you would be well served to ignore all advice from daFei. He has a long history of posting incomprehensible, incorrect, irrelevant nonsense.
jverda at 2007-7-14 17:23:55 >

I'm a little confused by your response jverd. Not that I don't understand what you're saying, but rather, why you are saying it. If I return a concrete List class such as ArrayList, then how am I hiding the implementation details from the caller? If the caller knows that he must cast the returned List to an ArrayList then he obviously knows the "implementation details" because otherwise he wouldn't know the concrete type to cast to. At any rate, I appreciate the response.
> I'm a little confused by your response jverd. Not
> that I don't understand what you're saying, but
> rather, why you are saying it. If I return a
> concrete List class such as ArrayList, then how am I
> hiding the implementation details from the caller?
You declare that you're returning a List. You have to return a concrete implementation, but the caller can't tell from the declaration what type you're returning.
public List foo() {
return new ArrayList();
}
> If the caller knows that he must cast the returned
> List to an ArrayList
He doesn't know that and he doesn't need to cast. He can just use the List, in the general case.
I mentioned this unusual-but-possible situation, just for thoroughness' sake. It's similar to List.toArray(Object[] input) public List foo(List input) {
List rslt;
// use reflection to create a new List of the same type as input
return rslt;
}
In this case, the caller still doesn't know from the declaration what the returned class will be, but the programmer writing the caller's code has read your docs where you said that the concrete class that's returned wil be the same as the concrete class that's been passed in.
jverda at 2007-7-14 17:23:55 >

> I'm a little confused by your response jverd. Not
> that I don't understand what you're saying, but
> rather, why you are saying it. If I return a
> concrete List class such as ArrayList, then how am I
> hiding the implementation details from the caller?
> If the caller knows that he must cast the returned
> List to an ArrayList then he obviously knows the
> "implementation details" because otherwise he
> wouldn't know the concrete type to cast to. At any
> rate, I appreciate the response.
You don't want to ensure the user's flexibility, but YOUR flexibilty. Maybe on some point you decide that for some reason (mabye performance) you want a linkedList implementation rather than an arrayList. You can do that without breaking existing code, because you declared your method signature as general as possible.
Imagine you are a package developer. You have shipped your first version, and some hundred customers are using your code. If you want to redesign your package and you break any method signature, ALL your customers have to recompile their code.
To avoid that, you force them to operate on a very abstract level. If they still want to use the subtype - well, they can use reflection and casting, but they do it on their own risk.
Writing against interfaces gives you the chance to make slight changes to the code without breaking existing implementations.
