путаница с параметризацией типа scala

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

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. Почему это происходит?


person Rahul    schedule 16.09.2015    source источник


Ответы (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]

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

person Gabriele Petronella    schedule 16.09.2015