WMix,
идея в том, что можно использовать поток ввода в файл и так:
Код:
FileOutput (new FileOutput)
.open(file_name)
.put(some_data)
.put( (new DataAccessor).flush() )
.close();
и вот так:
Код:
Interface AbstractDataProvider
+ populate(output_stream: &Output): &AbstractDataProvider
end;
FileOutput file_output = new FileOutput;
abstract_data_provider.populate(file_output);
file_output.close();
То есть, мы можем избавиться, в определённых ситуациях, от несущественных точек следования.
Можно привести и ещё более интересный пример, когда объект является неизменяемой коллекцией других объектов.
У меня нечто подобное в коде было реализовано примерно год назад, в прошлом августе.
Объект интерполятор является одновременно коллекцией узлов интерполяции, то есть копирует к себе сетку. Соответственно, интерполяторы разной степени гладкости возвращают ссылки на узлы, которые содержат всё больше и больше информации.
Примерно так это выглядело, хотя точно сейчас уже не скажу.
Код:
struct Node {
double pos;
};
struct FirstOrderNode: public Node {
double value;
};
struct SecondOrderNode: public FirstOrderNode {
double derivative;
};
struct Interpolator {
const Node& operator[](std::size_t) const throw(std::out_of_range) =0;
};
class FirstOrderInterpolation: public virtual Interpolator {
public:
const FirstOrderNode& operator[](std::size_t i) const throw(std::out_of_range);
};
class SecondOrderInterpolation: public Interpolator {
const SecondOrderNode& operator[](std::size_t i) const throw(std::out_of_range);
};
Ещё, кстати важно для Java, ковариантность распространяется на исключения. Наследник может выкидывать специфические для себя исключения, если эти исключения потомки тех, которые указаны в интерфейсе-родителе.
AnrDaemon,
есть разные возможности, почему у Вас что-то не сработало. Потоки ввода/вывода имеют сильные побочные эффекты, потому организация ковариантных интерфейсов -- непростое дело. Но не невозможное! А в использовании удобно и прозрачно.
Самая распространённая причина:
Interface Output на самом деле является частичной реализацией ввода над абстракцией буфера. Многим кажется, что так можно сэкономить код (повторное использование). В итоге: FileOutput -- это фотоаппарат, к которому клейкой лентой прикрутили калькулятор.
Если Output -- абстракция, а не реализация, то проблем не должно возникать. Да, потоки имеют побочные эффекты и доказать в три строчки строго не получится. Но, опять же, есть масса задач, где можно уйти от побочных эффектов, тогда уже компилятор сам, без вашего участия, сможет определить, что интерфейс ковариантный.
Скажем в Haskell или ML, вам не придётся явно указывать возвращаемый тип, компилятор сам для Вас выведет FooImpl.