简体   繁体   English

映射成员使用存在类型键入的类的scala类型错误

[英]scala type error for class with map member typed with existential type

I have a type system problem with scala that I can't understand. 我在Scala中遇到无法理解的类型系统问题。 The problem is illustrated with the snippet below. 下面的代码段说明了该问题。 What is the correct approach to be able to have a map member in a class that can have keys of type A or subtypes of A? 能够在具有键类型为A或A的子类型的类中拥有映射成员的正确方法是什么? I think the second approach is correct but I get errors when I try to get elements from the map. 我认为第二种方法是正确的,但是当我尝试从地图中获取元素时出现错误。 Has this something to do with existential types? 这与存在类型有关吗?

sealed abstract class MyBaseType
case class Concrete1() extends MyBaseType
case class Concrete2() extends MyBaseType

case class DictVariant1(data: Map[MyBaseType, Double])
case class DictVariant2(data: Map[_ <: MyBaseType, Double])

object App {
  def main(args: Array[String]) {
    val d = List((Concrete1(), 3.5)).toMap

    /* this fails with:
     * type mismatch;  
     * found: scala.collection.immutable.Map[Concrete1,Double]  
     * required: Map[MyBaseType,Double] 
     * Note: Concrete1 <: MyBaseType, but trait Map is invariant in type A. 
     * You may wish to investigate a wildcard type such as `_ <: MyBaseType`. 
     * (SLS 3.2.10)
     * 
     */
    val dv1 = DictVariant1(d)
    dv1.data.get(d)

    /* Works fine */
    val dv2 = DictVariant2(d)

    /* this fails with:
     * type mismatch;  
     * found: d.type (with underlying type scala.collection.immutable.Map[Concrete1,Double])  
     * required: _$1    
     * 
     */
    dv2.data.get(Concrete1())
  }
}

Unless you explicitly require getting the keys back out as the statically correct subtype (which seems unlikely), then the following will work: 除非您明确要求将密钥作为静态正确的子类型取回(这似乎不太可能),否则以下方法将起作用:

case class DictVariant1(data: Map[MyBaseType, Double])
val d = List((Concrete1() : MyBaseType, 3.5)).toMap
val dv1 = DictVariant1(d)

Using a type ascription forces Concrete1() to be seen as an instance of the base type; 使用类型描述强制将Concrete1()视为基本类型的实例; you'd get the same effect if you added an explicit type to d : 如果向d添加显式类型,则将获得相同的效果:

val d : Map[MyBaseType, Double] = ....

Or if you put multiple entries in such that the type inferencer picks the base type: 或者,如果您放入多个条目,以便类型推断器选择基本类型:

val d = List((Concrete1(), 3.5), (Concrete2(), 4.5)).toMap

The 'get' you use is always going to fail, however, because you're trying to use the whole Map as an index: it works fine if you provide a sensible key: 但是,您使用的“获取”总是会失败,因为您正试图将整个Map用作索引:如果您提供了合理的键,它会很好地工作:

dv1.data.get(Concrete1())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM