【问题标题】:Avoiding instanceof with Generics使用泛型避免 instanceof
【发布时间】:2012-08-13 21:37:10
【问题描述】:

我需要添加到子属性列表(ProductOption 和 ProductAttribute),这些子属性是名为 Product 的父对象的属性。这三个类都扩展了一个抽象类 CMS。

我想一般地调用方法“attachChildToParent”,但我通过推迟instanceof 并强制转换为产品来延迟不可避免的事情。

有没有办法可以通用地写这个,这样我就可以避免演员表?

测试:

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);         
        }
    }
}

产品类(父级)

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);
            }
        }
    }
}

儿童班 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;
    }
}

儿童班 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;
    }
}

【问题讨论】:

  • 我已经很久没有使用java了,但是在C#中你可以这样声明:FatherType foo = new ChildType();和列表

标签: java generics


【解决方案1】:

您可以使用访问者模式来解决强制转换问题:在您的 CMS 类中,添加一个新的(抽象)方法 attachTo(Product parent)。在每个子类中,都可以实现这个方法调用父类的attach,正确的函数就会被解析。

【讨论】:

    【解决方案2】:

    覆盖?

    public void attach(ProductOffering cms) {
       this.getLpo().add(cms);
    }
    
    public void attach(ProductAttribute cms) {
       this.getLpa().add(cms);
    }
    

    【讨论】:

    • 这段代码已经存在:问题是调用站点只知道超类,它不会在运行时消除歧义。
    • 噢!很抱歉快速回答我只是看了一眼,你是对的。由于运行时类型擦除,我认为泛型不会有帮助。我会考虑一下,但是因为您有一个逻辑分支,所以当将实际实现传递给方法时,您需要在运行时区分实际实现。您可能需要重新考虑这个问题。
    【解决方案3】:

    委托给子类:

    public void attach(CMS cms) {
        cms.callAdd(this);
    
    }
    

    在 CMS 上添加:

    public abstract void callAdd(Product product);
    

    在 ProductOffering 上添加:

    public void callAdd(Product product) {
       getLpo().add(this)       
    }
    

    ProductAttribute 类似...

    【讨论】:

      【解决方案4】:

      有一些方法可以避免这种情况,但不能使用泛型。泛型允许您编写允许避免强制转换的代码,但是当您需要避免 instanceof 时它无济于事。主要原因是所有具有泛型类型的变量在内部都被视为Object

      这就是为什么当您从attachChildToParent() 调用它时编译器使用通用的attach(CMS) 方法:要附加的子项的类型将是CMS - 编译器无法保留该类型你打电话时使用attachChildToParent()

      唯一的解决办法是在children中写一个attach方法:

      public class ProductAttribute extends CMS {
          @Override
          public void attachTo( Product p ) {
              p.getLpo().add( this );
          }
      }
      

      【讨论】:

      • 谢谢。那是我最初的方法,但它假定父级始终是 Product。实际上,情况可能总是如此,我想多了。但这个谜题确实困扰着我。
      • 您还可以创建一个接口并将其用作attachTo() 中的参数类型。这将允许您说“这个孩子可以附加到任何具有 getLpo() 方法的东西”
      【解决方案5】:

      泛型无法解决您的问题。由于您在 &lt;? extends CMS&gt; 之后丢失了实例特定信息,因此 List 中的所有元素都是 CMS 的子级。

      为什么不添加一个简单的方法,将两个列表作为参数,既适合您的设计,又能解决您的问题,它也很有效

      public void attach(List<ProductAttribute> lpa, List<ProductOffering> lpo) {
          this.lpa.addAll(lpa);
          this.lpo.addAll(lpo);
      }
      

      【讨论】:

        猜你喜欢
        • 2012-04-18
        • 2022-11-15
        • 1970-01-01
        • 1970-01-01
        • 2011-09-03
        • 2013-03-05
        • 1970-01-01
        • 1970-01-01
        • 2021-06-11
        相关资源
        最近更新 更多