scala 类型参数化混淆
scala type parameterization confusion
考虑以下示例
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
的子类
但是在下一行中,当我尝试调用以国家/地区作为参数类型(而不是预期的 Lookup)的方法时,出现 Type mismatch
错误。为什么会这样?
countryValue
是从 Country
到 String
的函数(实际上是一种将 eta 扩展为函数的方法,但现在不相关),即 Function1[Country, String]
.
render
期望 Function1[Lookup, String]
.
所以我们要回答的问题是
Given Country
is a subtype of Lookup
is Function1[Country, String]
a subtype of 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]
换句话说,当一个函数承诺不少于(协变 return 类型)并且不需要更多(反变输入类型)时,它就是另一个函数的子类型。
例如:只要函数接受 Dog
和 [=62=,就可以使用接受 Animal
和 return 的函数和 Apple
]s 需要 Fruit
。更正式
Dog <: Animal
Fruit <: Apple
Function1[Animal, Apple] <: Function1[Dog, Fruit]
但反之则不然:如果您的函数处理狗和 return 任何水果,它肯定不能用于处理任何动物和 return 苹果。
考虑以下示例
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
但是在下一行中,当我尝试调用以国家/地区作为参数类型(而不是预期的 Lookup)的方法时,出现 Type mismatch
错误。为什么会这样?
countryValue
是从 Country
到 String
的函数(实际上是一种将 eta 扩展为函数的方法,但现在不相关),即 Function1[Country, String]
.
render
期望 Function1[Lookup, String]
.
所以我们要回答的问题是
Given
Country
is a subtype ofLookup
is
Function1[Country, String]
a subtype ofFunction1[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]
换句话说,当一个函数承诺不少于(协变 return 类型)并且不需要更多(反变输入类型)时,它就是另一个函数的子类型。
例如:只要函数接受 Dog
和 [=62=,就可以使用接受 Animal
和 return 的函数和 Apple
]s 需要 Fruit
。更正式
Dog <: Animal
Fruit <: Apple
Function1[Animal, Apple] <: Function1[Dog, Fruit]
但反之则不然:如果您的函数处理狗和 return 任何水果,它肯定不能用于处理任何动物和 return 苹果。