0

I have an abstract class called ProjectItem and I have 2 specific classes. One of them is called Task and the other is called Deliverable.

I have to put all the ProjectItems inside a list, which I did, and I also need to put all the ProjectItems that are Deliverable inside another list.

My approach is to, so to say, detect the ProjectItems that are also being extended by a Deliverable and put them in a list. I have tried using a for loop like this:

public List<ProjectItem> projectItems = new ArrayList<ProjectItem>();
public List<Deliverable> allDeliverables = new ArrayList<Deliverable>();

public List<Deliverable> allDeliverables() {
    for(ProjectItem pItem : projectItems) {
        if(pItem instanceof Deliverable) {
            allDeliverables.add(pItem);
        }
    }

    return allDeliverables;
}

The thing is that I get an error because allDeliverables as a list accepts only Deliverable and also the use of instanceof is not correct but I don't see any other way around.

4
  • 1
    I'm no Java expert, but shouldn't you be casting pItem to Deliverable? allDeliverables.add((Deliverable) pItem);
    – Rik D
    Jul 14 '20 at 11:50
  • And your for-loop should probably use an object instead of ProjectItem: for(Object pItem : projectItems)
    – Rik D
    Jul 14 '20 at 11:51
  • 4
    Btw I think the question is a better fit for stackoverflow
    – Rik D
    Jul 14 '20 at 11:53
  • 3
    If you call the allDeliverables method twice, you'll re-add deliverable project items to the list. It might be better to add them to the list as they are created or put onto the project items list rather than waiting until later. If you can, use tell instead of ask -- for example, tell the items to add themselves to the proper list(s).
    – Erik Eidt
    Jul 14 '20 at 16:14
2

You just need to cast pItem to Deliverable using allDeliverables.add((Deliverable) pItem);

I can not stress enough how helpful IDEs are in dealing with this. For example, eclipse offers you this as an easy quick fix (the first one):

Eclipse offers the cast as a quick fix

All other IDEs should offer similar suggestions, if you are using an IDE that doesn't offer this as a trivial fix, ditch it and use a better one.

In the future, once pattern matching is a proper feature of the language (slated for Java 16), you will be able to leverage it as this is one of the classical examples (instanceof, then casting) for which it is designed:

if (pItem instanceof Deliverable deliverable){
    allDeliverables.add(deliverable);
}

Another option is to use streams and filter out incompatible classes, then map to the correct type, then collect:

public List<Deliverable> allDeliverables(){
    return projectItems.stream()
        .filter(Deliverable.class::isInstance)
        .map(Deliverable.class::cast)
        .collect(Collectors.toList());
}
1

The answer of @Polygnome is correct an a practical approach to your problem. I especially like the the "streams approach".

However, I'd like to show a more OO-ish approach:

private static final
Biconsumer<Collection<? extends ProductItem>, ProductItem> DO_NOTHING = (l,i)->{;};
private final    
Map<Class<?>,BiConsumer<Collection<? extends ProductItem>, ProductItem>> itemCollectors = new HashMap<>();

// in constructor
itemCollectors.put(Deliverable.class,(list,item)-> list.add(item));

public List<Deliverable> allDeliverables(){
   for(ProjectItem pItem : projectItems)
      itemCollectors.getOrDefault(pItem.getClass(),DO_NOTHING)
                    .accept(allDeliverables,pItem)
   }
}

Alternatively the concrete classes could know themselves if they would be part of the collection or not:

Class ProjectItem would define another abstract method:

abstract public <T extends ProjectItem> void addTo(Collection<T> theCollection);

The concrete classes would implement it as needed:

// non deliverable sub class
public <NonDeliverable> void addTo(Collection<NonDeliverable>  theCollection){
} //  just empty


//  deliverable sub class
public <Deliverable> void addTo(Collection<Deliverable>  theCollection){
   theCollection.add(this);
}

// usage
public List<Deliverable> allDeliverables(){
   for(ProjectItem pItem : projectItems)
     pItem.addTo(allDeliverables);
   }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.