Здравствуйте, palm mute, Вы писали:
PM> Собственно, с этим все понятно и у меня никогда проблем с ковариантностью не было. А вот контравариантность -- она вроде как есть, даже в стандартной библиотеке Scala используется:
E>>E>>abstract trait Function1 [-T1, +R] extends java.lang.Object with scala.ScalaObject
E>>
E>>но, блин, сходу вспомнить, что она означает и придумать какой-то пример ее использования -- не получается.
PM>Что такое Function1, довольно просто объяснить.
За ссылочки спасибо, за попытку объяснения Function1 так же, хотя путаница с типами вызвала свои сложности
.
Вообще же хороший пример контравариантности есть где-то в ScalaByExample или ScalaOverview, или ScalaReference, или ScalaTutorial (вообще, что касается документации, то одни и те же фрагмены, имхо, кочуют из документа в документ, практически 1 в 1
):
Итак, пусть у нас есть обобщенный класс:
class IOChannel[-T] { ... }
который позволяет записывать в коммуникационный канал какие-то объекты.
Далее, у нас есть функция, которая позволяет записать некий отчет (список строк) в коммуникационный канал, принимающий строки:
def storeReportTo( report: List[String], receiver: IOChannel[String] ) { ... }
Самый простой способ использования storeReportTo -- это передача ему объекта именно IOChannel[String]:
def openOrCreateReportFile(): IOChannel[String ] = { ... }
val report: List[String] = buildReport
val reportFile: IOChannel[String] = openOrCreateReportFile
storeReportTo( report, reportFile )
Но, пусть где-то у нас есть объект IOChannel[AnyRef] -- поскольку он может записывать любой объект, значит и строи он способен записывать. Значит, его можно попробовать передать в storeReportTo:
def findAppropriateTargetChannel(): IOChannel[AnyRef] = { ... }
val report: List[String] = buildReport
val reportChannel = findAppropriateTargetChannel
storeReportTo( report, reportChannel )
Вызов storeReportTo в этом случае оказывается легальным, поскольку благодоря контравариантности IOChannel[AnyRef] считается производным от IOChannel[String] -- т.е. способен использоваться там, где требуется IOChannel[String]. При всем при том String является производным от AnyRef.
Вот такая вот картинка. В принципе, если сосредоточится и направить мозги в нужную сторону, то понимание восстанавливается довольно просто. Сосредоточится не всегда удается, тем более что в документации по Scala этот вопрос не очень хорошо проиллюстирован.
Но с ко/контравариантностью в Scala есть и еще другие сложности. Там есть такие понятия, как ковариантные позиции аргументов и контравариантные. К сожалению, это дело описывается только в ScalaReference и очень формальным языком (для меня, как дилетанта в математике это вообще очень тяжело читается). Вот здесь уже начинается гадание. Так, в ScalaReference приводится пример:
// Вот такое использование аргумента x в методе append недопустимо:
abstract class Vector[+a] {
def append(x: Vector[a]): Vector[a]
// **** error: illegal variance:
// ‘a’ occurs in contravariant position.
}
// Должно быть так:
abstract class Vector[+a] {
def append[b >: a](x: Vector[b]): Vector[b]
}
Когда начинаешь разбираться с частностями, до всего, вроде как, доходишь. Но вот нормальной целостной картины пока нет