Java如何避免类型转换

时间:2018-11-27 13:14:17

标签: java generics casting

我过去曾几次遇到此问题,但还没有真正找到好的解决方案/设计。

下面的示例代码将从实体(公司或文章)生成PDF文档

public class Entity
{
    int id;
}

public class Company extends Entity
{
    private String HQ;
}

public class Article extends Entity
{
    private String title;
}

public interface EntityPDFGenerator
{
    void generate(Entity entity);
}

public class ArticlePDFGenerator implements EntityPDFGenerator
{
    public void generate(Entity entity)
    {
        Article article = (Article) entity;
        // create Article related PDF from entity
    }
}

public class CompanyPDFGenerator implements EntityPDFGenerator
{
    public void generate(Entity entity)
    {
        Company company = (Company) entity;
        // create Company related PDF
    }
}

主类:

public class PDFGenerator
{
    public void generate(Entity entity)
    {
        EntityPDFGenerator pdfGenerator = getConcretePDFGenerator(entity);
        pdfGenerator.generate(entity);

    }

    // lets make the factory task simple for now
    EntityPDFGenerator getConcretePDFGenerator(Entity entity)
    {
        if(entity instanceof Article){
            return new ArticlePDFGenerator();
        }else{
            return new CompanyPDFGenerator();
        }
    }
}

在上述方法中,问题在于将Entity强制转换为具体类型(在代码的后期阶段进行铸造可能很危险)。我尝试使用泛型来实现,但随后得到警告

  

未选中的对“ generate(T)”的调用

我可以改善此代码吗?

3 个答案:

答案 0 :(得分:1)

在这里,您可以进行建议的更改:

public interface EntityPDFGenerator<T extends Entity> {
    void generate(T entity);
}


public class ArticlePDFGenerator implements EntityPDFGenerator<Article> {

    public void generate(Article entity)
    {
        // create Article related PDF from entity
    }
}


public class CompanyPDFGenerator implements EntityPDFGenerator<Company> {

    public void generate(Company entity)
    {
        // create Company related PDF
    }
}

答案 1 :(得分:0)

我建议在实体类中移动getGenerator()方法,并在公司和商品类中覆盖它。 当然,除非有充分的理由不这样做。

答案 2 :(得分:0)

简短答案

在这里泛型不是正确的工具。您可以使转换内容明确:

public class CompanyPDFGenerator implements EntityPDFGenerator
{
    public void generate(Entity entity)
    {
        if (! (entity instanceof Company)) {
            throw new IllegalArgumentException("CompanyPDFGenerator works with Company object. You provided " + (entity == null ? "null" : entity.getClass().getName()));
        }
        Company company = (Company) entity;
        System.out.println(company);
        // create Company related PDF
    }
}

或者您可以在实体类中定义某种数据结构,并仅在打印机中使用它:

public abstract class Entity
{
    int id;
    public abstract EntityPdfData getPdfData();
}

// ...

public class CompanyPDFGenerator implements EntityPDFGenerator
{
    public void generate(Entity entity)
    {
        EntityPdfData entityPdfData = entity.getPdfData();
        // create Company related PDF
    }
}

长答案

如果您在编译时知道类型,泛型将很有用。即如果您可以将实际类型写入程序。对于列表,它看起来很简单:

// now you know at compile time that you need a list of integers
List<Integer> list = new ArrayList<>();

在您的示例中,您不知道:

public void generate(Entity entity)
{
    // either Article or Company can come it. It's a general method
    EntityPDFGenerator pdfGenerator = getConcretePDFGenerator(entity);
    pdfGenerator.generate(entity);

}

假设您要向EntityPDFGenerator添加类型,如下所示:

public static interface EntityPDFGenerator<T extends Entity>
{
    void generate(T entity);
}

public static class ArticlePDFGenerator implements EntityPDFGenerator<Article>
{
    public void generate(Article entity)
    {
        Article article = (Article) entity;
        // create Article related PDF from entity
    }
}

public static class CompanyPDFGenerator implements EntityPDFGenerator<Company>
{
    public void generate(Company entity)
    {
        Company company = (Company) entity;
        // create Company related PDF
    }
}

这看起来不错。但是,找到合适的发电机将非常棘手。 Java泛型是不变的。甚至ArrayList<Integer>也不是ArrayList<Number>的子类。因此,ArticlePdfGenerator不是EntityPDFGenerator<T extends Entity>的子类。即它将无法编译:

<T extends Entity> EntityPDFGenerator<T> getConcretePDFGenerator(T entity, Class<T> classToken)
{
    if(entity instanceof Article){
        return new ArticlePDFGenerator();
    }else{
        return new CompanyPDFGenerator();
    }
}