I have three interfaces:
public interface Combinable<V> {
V add(V other);
}
public interface Sublistable<V> {
boolean hasSublist();
List<V> getSublist();
void setSublist(List<V> sublist);
}
public interface HasUniqueIdentifier {
String getUniqueIdentifier();
}
and 4 classes that implement some or all of these interfaces:
public class Grandparent implements HasUniqueIdentifier,
Sublistable<Parent>,
Combinable<Grandparent>
{ List<Parent> sublist; ... }
public class Parent implements HasUniqueIdentifier,
Sublistable<Child>,
Combinable<Parent>
{ List<Child> sublist; ... }
public class Child implements HasUniqueIdentifier,
Sublistable<Grandchild>,
Combinable<Child>
{ List<Grandchild> sublist; ... }
public class Grandchild implements HasUniqueIdentifier,
Combinable<Grandchild>
{ }
I would like to have a generic method that does the following:
public <V, T extends HasUniqueIdentifier & Combinable<T> & Sublistable<V>>
List<T> combine(List<T> items) {
Multimap<String, T> similarItemMap = HashMultimap.create();
for (T item: items) {
similarItemMap.put(item.getUniqueIdentifier(), item);
}
List<T> output = new ArrayList<T>();
for (Collection<T> similarCollection : similarItemMap.asMap().values()) {
List<T> similarItems = Lists.newArrayList(similarCollection);
T source = similarItems.get(0);
for (int i = 0; i < similarItems.size(); i++) {
source = source.add(similarItems.get(i));
}
output.add(source);
}
for (T item : output) {
if (item.hasSublist()) {
item.setSublist(combine(item.getSublist));
}
}
return output;
}
Realizing that this could create an infinite loop (unless the bottom class -- Grandchild -- implemented Sublistable and set hasSublist() { return false; }
, or something), as well as the fact that this method is kind of crazy with generics, I have to ask: Is there some way that I can rewrite this a bit so that I can call the method in this way:
combine(listOfGrandparents)
or should I give up on this being one method and try to refactor it in a better way?
Edit:
To better explain what it is that I'm trying to do, I have a list of objects of type A
. Each object a
has a list of objects of type B
. Each object b
has a list of objects of type C
, and so on, until eventually type T
(for some varying level of T
) doesn't have a sublist anymore.
Each type basically has three things that it needs to do for a "merge" or "combine" method:
item.getUniqueIdentifier()
methodsource.add(other)
methodSince each item behaves so similarly, it would be nice if I could use a single method instead of having to have n
methods, one for each type. Unfortunately, since none of the types are guaranteed to be the same (other than having implemented some or all of the given interfaces above), creating a generic method proves to be difficult. Is there some way to do this that I'm missing?
public <V extends HasUniqueIdentifier & Combinable<V>,
T extends HasUniqueIdentifier & Combinable<T>>
List<T> combine(List<T> items) {
Multimap<String, T> similarItemMap = HashMultimap.create();
for (T item: items) {
similarItemMap.put(item.getUniqueIdentifier(), item);
}
List<T> output = new ArrayList<T>();
for (Collection<T> similarCollection : similarItemMap.asMap().values()) {
List<T> similarItems = Lists.newArrayList(similarCollection);
T source = similarItems.get(0);
for (int i = 0; i < similarItems.size(); i++) {
source = source.add(similarItems.get(i));
}
output.add(source);
}
for (T item : output) {
if (item instanceof Sublistable<?>) {
@SuppressWarnings("unchecked")
Sublistable<V> sublistableItem = ((Sublistable<V>)sublistableItem);
if (sublistableItem.hasSublist()) {
sublistableItem.setSublist(combine(sublistableItem.getSublist));
}
}
}
return output;
}
Unfortunatey, this method requires both a @SupressWarnings
and an instanceof
, which I would like to avoid if possible. I haven't found anything else, though, yet.
Your first solution is fine, given that GrandChild implements Sublistable<Void>
vacuously. The tree is more uniform and easier to handle recursively. You may also want to have one merged interface Node<T,V> extends HasUniqueIdentifier, Combinable<T>, Sublistable<V>{}
If you don't want GrandChild implements Sublistable
, your 2nd solution is also fine. A single instanceof
to test a marker interface isn't a sin. The code can be rewritten as
public <T extends HasUniqueIdentifier & Combinable<T>>
List<T> combine(List<T> items)
{
...
List<T> output = new ArrayList<T>();
...
for (T item : output) {
if (item instanceof Sublistable<?>)
combineSublist((Sublistable<?>)item);
return output;
}
private <V> void combineSublist(Sublistable<V> sublistableItem)
{
if (sublistableItem.hasSublist()) {
sublistableItem.setSublist(combine(sublistableItem.getSublist));
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With