Путаница с параметризацией типа скалы

Рассмотрим следующий пример

abstract class Lookup(val code:String,val description:String)

class USState(code:String, description:String, val area:Symbol)
  extends Lookup(code,description)

class Country(code:String, description:String, val postCode:String)
  extends Lookup(code,description)

class HtmlLookupSelect(data:List[Lookup]) {
  def render( valueMaker:(Lookup) => String ) =
    data.map( (l) => valueMaker(l) )
}

val countries = List(
  new Country("US","United States","USA"),
  new Country("UK","Unites Kingdom","UK"),
  new Country("CA","Canada","CAN"))

def lookupValue(l:Lookup) = l.description
def countryValue(c:Country) = c.description + "(" + c.postCode + ")"

val selector = new HtmlLookupSelect(countries) //Doesn't throw an error
selector.render(countryValue) //Throws an error

HtmlLookupSelect ожидает список объектов Lookup в качестве параметра конструктора. При создании объекта HtmlLookupSelect ему передается список объектов округа, и компилятор не выдает ошибку, поскольку он распознает Country как подкласс Lookup

Но в следующей строке, когда я пытаюсь вызвать метод с Country в качестве типа параметра (вместо ожидаемого Lookup), я получаю Type mismatch ошибка. Почему это происходит?

1 ответ

Решение

countryValue это функция от Country в String (на самом деле это метод, который становится расширенным в функцию, но не актуален сейчас), т.е. Function1[Country, String],

render ожидает Function1[Lookup, String],

Итак, вопрос, на который мы хотим ответить

Дано Country это подтип Lookup

является Function1[Country, String] подтип Function1[Lookup, String]?

Чтобы ответить на это, давайте посмотрим на определение Function1:

trait Function1[-T1, +R] extends AnyRef

Увидеть -T1? Это входной параметр и - Значит это Function1 является контравариантным по своему входному параметру и ковариантным по своему выходному параметру.

Так что если A <: B (где <: это отношение подтипа) и R <: Sзатем

Function1[B, R] <: Function[A, S]

В вашем примере Country <: Lookup так

Function1[Country, String] </: Function[Lookup, String]

Другими словами, функция является подтипом другой, когда она обещает не меньше (ко-вариантный тип возврата) и больше не требует (противоположно-вариантный тип ввода).

Например: функция, которая принимает Animal и возвращает Apple может быть использован всякий раз, когда функция, которая принимает Dog и возвращает Fruit необходимо. Более формально

Dog <: Animal
Fruit <: Apple
Function1[Animal, Apple] <: Function1[Dog, Fruit]

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

Другие вопросы по тегам