То, что вы видите, называется контравариантностью . Из-за этого параметры функции должны быть противоположными:
class HisSubClass extends MyClass
val his = new HisSubClass
f7(his) // his is accepted as MyClass
Теперь f4
будет вызываться с чем-то, что не MySubClass
, что будет ошибкой.
Случай Seq
/ List
работает, потому что все наоборот. List
является подклассом Seq
.
val af2: (List[_]) => Boolean = af0
похоже на
val aff0: (MyClass) => Boolean = (s) ⇒ { s eq null }
val aff2: (MySubClass) => Boolean = aff0
Обещание (контракт)
Что помогло мне понять разницу между параметром и возвращаемым значением, так это думать о объявлениях типа как об обещаниях (или контрактах). Возвращаемое значение является ковариантным, поскольку вы обещали, что возвращаемое значение будет иметь тип MyClass
, и, предоставив MySubClass
в подклассе, вы все еще выполняете свое обещание. Обещая, вы примете параметр типа MyClass
, а затем попытка объявить члена подкласса, принимающего только MySubClass
, означает попытку сузить обещание, чего вы не можете (подкласс должен полностью реализовать родительский класс).
С вашим примером в f4
вы пообещали, что функции будет присвоен MySubClass
в качестве параметра. Когда вы пытаетесь присвоить это f7
, вы пытаетесь нарушить это обещание, поскольку вы можете передать любое MyClass
f4
, вызвав его через f7
.