views:

307

answers:

7

Say I have this class :

public class BaseJob{

String name;

public void setName(String name){
this.name=name;
}
public String getName()
{
   return name;
}
}

and another class that extends it :

public class DetailedJob extends BaseJob{

public void doThing();

}

Furthermore, I have this method in another class :

List<BaseJob> getSomeJobs()

Now, my problem is :

is it possible to avoid to cast each item sequentially in the returned list of getSomeJobs, if I know for sure that every BaseJob returned is indeed a DetailedJob ?

Put differently, is there another solution than the following to cast all items in the list :

List<BaseJob> baseJobList = getSomeJobs(); 
List<DetailedJob> detailedJobList = new ArrayList<DetailedJob>();
for (BaseJob baseJob : baseJobList)
    detailedJobList.add((DetailedJob) baseJob);
A: 

You could create a parameterized getSomeJobs method to take in an argument saying that you know everything is a DetailedJob, meaning that it would return a DetailedJob list rather than the base class.

If you use instanceof, you wouldn't even need to cast, you could just ask if each element is an instance of a DetailedJob and proceed for there. This is almost no better than looping through each object and casting, however.

AlbertoPL
+1  A: 

Well, there's:

List<BaseJob> baseJobList = getSomeJobs(); 
@SuppressWarnings("unchecked")
List<DetailedJob> detailedJobList = (List) baseJobList;

The downside of this is that if any of the jobs in the list aren't detailed jobs, the exception will only be thrown when someone tries to fetch it. Also, if a new non-detailed job is added to baseJobList afterwards, that could screw up anyone using detailedJobList. Basically you've lost a lot of type safety. In some cases you may not care, but it's not something you should do lightly.

Jon Skeet
I was tempted to give this answer but this is really dangerous, as you correctly noted
dfa
The cast should be to `(List)` not `(List<DetailedJob>)`. Otherwise, it wouldn't compile.
notnoop
You generally need a double cast for impossible casts.
Tom Hawtin - tackline
@msaeed: Thanks, will fix.
Jon Skeet
This is going to work for me, even if I understand the type safety problem. Thanks.
madewulf
A: 

Make getSomeJobs() or write another function getSomeDetailedJobs() that returns List < DetailedJob> instead of List < BaseJob>. I dont know how else we can be "sure" about all elements being of type DetailedJobs.

Sathya
... till I saw the reply from Jon Skeet !
Sathya
"returns List instead of List" <- interesting...
geowa4
oops sorry corrected.
Sathya
+4  A: 

If you know that all of the jobs are going to be detailed jobs, why would you put them in an arraylist of basejobs? There's no reason to, and that method would eliminate many possible errors and exceptions.

CrazyJugglerDrummer
Because the class holding getSomeJobs works with BaseJob only and does not want to know if those baseJobs are detailedJobs or anything else.
madewulf
But they are basejobs, and they can be used for anything a basejob can.
CrazyJugglerDrummer
+6  A: 

Probably what you want to do is parameterising the class that defines getSomeJobs.

public final class JobHolder<T extends BaseJob> {
    public List<T> getSomeJobs() {
        ...

Generally unchecked casts indicate a design problem. They are unavoidable in certain situations such as low-level implementations and when dealing with serialisation.

Tom Hawtin - tackline
This seems as good solution. Unfortunately, I am not allowed to change the code of JobHolder, as you named it,
madewulf
This will be used as: JobHolder<DetailedJob> jobHolder = new JobHolder<DetailedJob>(); List<DetailedJob> jobs = jobHolder.getSomeJobs();
Mnementh
A: 

While it doesn't directly solve your casting problem I'd be temped to use two methods on the 'other class':

List<BaseJob> getAllJobs();

and

List<DetailedJob> getDetailedJobs();

This makes your code more readable to anyone using the 'other class' and will hopefully prevent mistakes.

Either that or I'd genericise the 'other class' like @Tom Hawtin suggests.

Nick Holt
A: 

Your other class that provides the getSomeJobs method should implement an interface (to help with your unit testing, among other things). Let's call it JobProvider. You can declare the interface such that it will always produce a list of something that extends a base job, and in subclasses where you know your job is always going to be of a certain sub-type, you can narrow the type definition there.

interface JobProvider {
   List<? extends BaseJob> getSomeJobs();
}

class JobProviderImpl implements JobProvider {
   public List<DetailedJob> getSomeJobs() {
      // do stuff and return
   }
}

Now, in other code, if you know you're dealing with a JobProviderImpl, you can case it and know that the list will contain only DetailedJobs.

if (provider instanceof JobProviderImpl) {
   List<DetailedJob> detailedJobs = ((JobProviderImpl) provider).getSomeJobs();
}
Steve Reed