Как обеспечить безопасность типов с помощью дженериков?

У меня есть класс

public class ReportItem<ReportType extends Report>{ }

и класс

public abstract class Report implements Iterable<ReportItem>{

    private List<ReportItem<? extends Report> > itemList;

    public void add(ReportItem<? extends Report> item){
        itemList.add(item);
    }

    //Some other staff

}

public class ConcreteReport extends Report{ 
    //Some staff
}

Дело в том, что метод add(ReportItem<? extends Report>) небезопасен в том, как я мог предоставить элементы, не привязанные к текущему отчету, но связанный с каким-то другим, и компилятор не будет жаловаться.

Можно ли написать метод add безопасным способом, т.е. мы могли бы передать в качестве аргумента только то, что ReportItem<T>, где T - тип текущего отчета.

Ответ 1

Я думаю, вы ищете следующее.

public abstract class Report<T extends Report<T>> implements Iterable<ReportItem<T>>{

    private List<ReportItem<T>> itemList;

    public void add(ReportItem<T> item){
        itemList.add(item);
    }

    //Some other stuff

}

public class ConcreteReport extends Report<ConcreteReport> { 
    //Some stuff
}

Принцип работы:

  • Вы хотите параметризовать ReportItem<T> с чем-то, что простирается от Report
  • Вы хотите убедиться, что список ReportItem<T> принадлежит одному типу Report

Чтобы связать параметр T ReportItem<T> с чем-то, что простирается от Report, вам необходимо параметризовать сам Report:

public abstract class Report<T> implements Iterable<ReportItem<T>>

вы добавляете связывание, которое необходимо расширить из отчета

public abstract class Report<T extends Report> implements Iterable<ReportItem<T>>

Но вы указываете привязку для необработанного типа отчета, который не работает, поэтому вам нужно предоставить Report параметр типа, который получает отчет, который равен T.

public abstract class Report<T extends Report<T>> implements Iterable<ReportItem<T>>

Таким образом вы можете параметризовать List<ReportItem<T>> с конкретным типом, который вы расширяете:

public class ConcreteReport extends Report<ConcreteReport> { 

Таким образом, список будет

public List<ReportItem<ConcreteReport>> itemlist;

Это то, что вы хотели.

И это работает!:) Я просто надеюсь, что мое объяснение этого имеет смысл.