Scala Variance Concept, почему он не компилируется - PullRequest
0 голосов
/ 05 июля 2018

Я новичок в Scala, поэтому, пожалуйста, не голосуйте "против".

class MyClass extends AnyRef
class MySubClass extends MyClass

val af0: (Seq[_]) => Boolean = (s) ⇒ { s eq null }

val f4: (MySubClass) => Boolean = (s) => { s eq null }

val af2: (List[_]) => Boolean = af0 //(Line 1)
val f7: MyClass => Boolean = f4 //(Line 2)

Почему строка (1) компилируется, а строка (2) - нет? Для меня они оба одинаковы, так как последовательность является подтипом списка. Как заставить это работать? как в случае строки 1?

https://docs.scala -lang.org / tutorials / FAQ / collection.html [Иерархия объектов списка]

Ответы [ 2 ]

0 голосов
/ 05 июля 2018

Поскольку вы пытаетесь присвоить значению типа Function1[MyClass, Boolean] значение типа Function1[MyClass, Boolean], но первый параметр типа Function1 противоречив, см. API doc :

trait Function1[-T1, +R] extends AnyRef

Но это позволяет вам сделать это:

val f7: MyClass => Boolean = s => s eq null 
val f44: (MySubClass) => Boolean = f7

Вы можете найти объяснение отклонений здесь .

0 голосов
/ 05 июля 2018

То, что вы видите, называется контравариантностью . Из-за этого параметры функции должны быть противоположными:

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.

...