Java Как избежать приведения типов - PullRequest
0 голосов
/ 27 ноября 2018

Я сталкивался с этой проблемой несколько раз в прошлом, но не нашел для нее хорошего решения / дизайна.

В следующем примере кода будет сгенерирован документ 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();
        }
    }
}

В приведенном выше подходе проблема заключается вприведение сущности к конкретному типу (приведение может быть опасным на более поздней стадии кода).Я пытался сделать это с помощью дженериков, но затем я получаю предупреждение

Не проверенный вызов 'generate (T)'

Могу ли я улучшить этот код?

Ответы [ 3 ]

0 голосов
/ 27 ноября 2018

Я бы предложил переместить метод getGenerator() в классе Entity и переопределить его в классах Company и Article.Если, конечно, нет веской причины не делать этого.

0 голосов
/ 27 ноября 2018

Краткий ответ

Дженерики здесь не тот инструмент.Вы можете сделать приведение явным:

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();
    }
}
0 голосов
/ 27 ноября 2018

Здесь вы идете с предлагаемыми изменениями:

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
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...