【问题标题】:Java How To Avoid Type CastingJava 如何避免类型转换
【发布时间】:2018-11-27 23:21:50
【问题描述】:

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

以下示例代码将从实体(公司或文章)生成 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();
        }
    }
}

在上述方法中,问题在于将实体转换为具体类型(在代码的后期进行转换可能很危险)。我试图用泛型来做,但后来我收到了警告

对“生成(T)”的未经检查的调用

我可以改进这段代码吗?

【问题讨论】:

  • PDFGenerater<T extends Entity> 怎么样?
  • 您的示例中没有一点通用代码,因此无法通过发布的代码获得该警告。

标签: java generics casting


【解决方案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
    }
}

【讨论】:

  • 非常感谢您的回答。问题:我如何根据实体类型动态实例化最终的 PDF 生成器,比如说工厂?
  • @gubak 这是一个不同的问题,是的。公认的答案指向正确的方法。另外,如果你想通过工厂创建许多不同类型的实例,那么你可以参考这个链接stackoverflow.com/questions/52950426/…
【解决方案2】:

简答

泛型在这里不是正确的工具。您可以明确地进行强制转换:

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

长答案

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

// 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&lt;Integer&gt; 也不是ArrayList&lt;Number&gt; 的子类。所以,ArticlePdfGenerator 不是EntityPDFGenerator&lt;T extends Entity&gt; 的子类。 IE。 这不会编译:

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

【讨论】:

  • “或者您可以在实体类中定义某种数据结构并仅在打印机中使用它:” - 至于我,Entity 不负责返回类似 @987654333 的内容@。如果应该添加更多的输出格式(TXT、JPG),那么每个Entity 类型都必须添加一个方法?这不是松散耦合的。
  • @LuCio 总的来说你是对的。然而,我们对整个代码库知之甚少,无法做出这样的决定。理想情况下,Entity 可以轻松访问所有需要的数据。如果是这样,那么我们不需要吸气剂。如果不是这样,那么我们可以有一个 getter,比如 getGenericEntityData()
【解决方案3】:

我建议在 Entity 类中移动 getGenerator() 方法并在 Company 和 Article 类中覆盖它。 当然,除非有充分的理由不这样做。

【讨论】:

  • 我也同意,除非涉及其他原因
猜你喜欢
  • 2020-01-18
  • 2019-10-04
  • 2014-11-26
  • 2020-12-29
  • 1970-01-01
  • 1970-01-01
  • 2020-12-05
  • 2022-01-22
  • 2022-07-31
相关资源
最近更新 更多