这很难说,所以请让我举个例子:
trait Cache
trait QueryLike {
type Result
}
trait Query[A] extends QueryLike {
type Result = A
def exec: Result
}
trait CachedQuery[A] extends QueryLike {
type Result = A
def execWithCache(cache: Cache): Result
}
def exec(query: QueryLike)(implicit cache: Cache): query.Result = query match {
case q: Query[query.Result] => q.exec
case cq: CachedQuery[query.Result] => cq.execWithCache(cache)
}
这种编译和运行正常,因为模式匹配是在不同类型( Query
, CachedQuery
)上完成的,而不是依赖于像this question这样的泛型 .
但我仍然得到编译器警告:
警告:(18,12)抽象类型类型模式中的结果A $ A4.this.Query [query.Result]未被选中,因为它被擦除情况q消除了:查询[query.Result] => q.exec
因为我无论如何都不直接使用依赖类型 query.Result
(比如将它转换为不同的操作),所以'd be ideal to just erase it completely and do away with the warning. But unfortunately, using wildcard doesn' t的工作原因很充分:
...
case q: Query[_] => q.exec // type mismatch
case cq: CachedQuery[_] => cq.execWithCache(cache)
...
有没有更好的方法来做到这一点而不产生编译器警告?
2 回答
此错误并非特定于路径相关类型 . 如果您尝试匹配任何
Query[A]
,您将得到相同的错误,因为类型参数A
在运行时被擦除 . 在这种情况下,它正在寻找's not possible that the type parameter can be anything other than the type you' . 由于Query[A]
是QueryLike { type Result = A}
,它也应该是Query[query.Result]
,尽管这是一种看起来有点不同寻常的方式 . 如果您愿意,可以使用@unchecked
注释来禁止警告:虽然很难说这是否适用于您的实际用例,但您也可以重新构建代码以避免完全匹配,并通过多态性更优雅地处理它(可能) . 由于最后一次
exec
无论如何都需要一个隐含的Cache
,所以对于每一个QueryLike
来说这似乎都不会受到伤害 . 您的API可以通过这种方式更加统一,并且您无需确定要调用的方法 .如果
Query[A]
需要没有Cache
的exec
,您还可以提供带有DummyImplicit
的重载,以允许它在没有DummyImplicit
的情况下工作 .实际上,问题似乎是路径依赖类型非常具体:问题是q和cq的类型 . q.Result是一个不兼容的查询类型 . 结果因为Scala类型检查器不知道,该查询和q和qc必须是相同的引用 .
因此,Query [query.Result]实际上确实需要运行时类型检查 . 我注意到了这一点,当我尝试删除类型参数并只使用内部结果时 . 然后模式匹配不再生成警告,但q.exec的返回类型将不与query.Result兼容 .
不使用@unchecked或asInstanceOf等的一种解决方案是将Result转换为QueryLike的类型参数 . 一般情况下,您不应该将抽象类型成员用于您想要“在上下文中”自由传递的内容 . 因此,在继承层次结构中的某个位置将类型成员转换为类型参数有点奇怪 .
所以这很好,正如编译器所知,他不必检查类型参数:
另一种方法是将方法添加到CachedQuery和Query的公共基本特征 .
要改变这一点,Scala可能必须能够确定两个稳定访问器何时相同 .