Reputation: 4827
I need to add to a List of child attributes (ProductOption and ProductAttribute) that are a property of a parent object called Product. All three classes extend an abstract class CMS.
I want to call the method "attachChildToParent" generically, but I am delaying the inevitable by deferring the instanceof
and cast to the Product.
Is there a way I can write this generically so I can avoid the cast?
To Test:
package puzzler;
import java.util.ArrayList;
import java.util.List;
public class Tester {
public static void main(String[] args) {
Product p = new Product();
ProductAttribute pa = new ProductAttribute();
ProductOffering po = new ProductOffering();
List<ProductAttribute> lpa = new ArrayList<ProductAttribute>();
List<ProductOffering> lpo = new ArrayList<ProductOffering>();
attachChildToParent(lpa, p);
}
static void attachChildToParent(List<? extends CMS> listChild, Product parent) {
for (CMS cmsItem : listChild) {
parent.attach(cmsItem);
}
}
}
The Product class (parent)
package puzzler;
import java.util.List;
abstract class CMS {
String node;
}
public class Product extends CMS {
List<ProductAttribute> lpa;
List<ProductOffering> lpo;
public List<ProductAttribute> getLpa() {
return lpa;
}
public void setLpa(List<ProductAttribute> lpa) {
this.lpa = lpa;
}
public List<ProductOffering> getLpo() {
return lpo;
}
public void setLpo(List<ProductOffering> lpo) {
this.lpo = lpo;
}
public void attach(ProductAttribute childNode) {
this.getLpa().add(childNode);
}
public void attach(ProductOffering childNode) {
this.getLpo().add(childNode);
}
// I want to avoid this. Defeats the purpose of generics.
public void attach(CMS cms) {
if (cms instanceof ProductOffering) {
this.getLpo().add((ProductOffering) cms);
} else {
if (cms instanceof ProductAttribute) {
this.getLpa().add((ProductAttribute) cms);
}
}
}
}
Child class 1
package puzzler;
import puzzler.CMS;
public class ProductAttribute extends CMS {
String node;
public String getNode() {
return node;
}
public void setNode(String node) {
this.node = node;
}
}
Child class 2
package puzzler;
import puzzler.CMS;
public class ProductOffering extends CMS {
String node;
public String getNode() {
return node;
}
public void setNode(String node) {
this.node = node;
}
}
Upvotes: 1
Views: 958
Reputation: 19185
Generics can not solve your problem. Since you loose instance specific information after <? extends CMS>
that all elements in List are children of CMS
only.
Why not add simple method which takes both list as parameters which fits in your design and solves your problem also it is efficient
public void attach(List<ProductAttribute> lpa, List<ProductOffering> lpo) {
this.lpa.addAll(lpa);
this.lpo.addAll(lpo);
}
Upvotes: 0
Reputation: 1018
Override?
public void attach(ProductOffering cms) {
this.getLpo().add(cms);
}
public void attach(ProductAttribute cms) {
this.getLpa().add(cms);
}
Upvotes: 0
Reputation: 328536
There are ways to avoid this but not with generics. Generics allow you to write code which allows to avoid casts but it doesn't help when you need to avoid instanceof
. The main reason is that all variables with a generic type are treated as Object
internally.
And this is why the compiler uses the generic attach(CMS)
method when you call it from attachChildToParent()
: The type of the children to attach will be CMS
- there is no way for the compiler to preserve the type that you use when you call attachChildToParent()
.
The only solution is to write an attach method in the children:
public class ProductAttribute extends CMS {
@Override
public void attachTo( Product p ) {
p.getLpo().add( this );
}
}
Upvotes: 0
Reputation: 7197
Delegate to the sub classes:
public void attach(CMS cms) {
cms.callAdd(this);
}
On CMS add:
public abstract void callAdd(Product product);
And on ProductOffering add:
public void callAdd(Product product) {
getLpo().add(this)
}
An similarly for ProductAttribute...
Upvotes: 2
Reputation: 40690
You can work around the casting by using the visitor pattern: in your CMS
class, add a new (abstract) method attachTo(Product parent)
. In each of the sub-classes, you can implement this method to call attach on the parent, and the right function will be resolved.
Upvotes: 5