这很难说,所以请让我举个例子:
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]未被选中,因为它被擦除消除 case q:查询[query.Result] => q.exec
由于我无论如何都不直接使用依赖类型query.Result
(比如将其转换为不同的操作),因此完全删除它并取消警告是理想的。但不幸的是,使用通配符并不是有用的原因:
...
case q: Query[_] => q.exec // type mismatch
case cq: CachedQuery[_] => cq.execWithCache(cache)
...
有没有更好的方法可以在不生成编译器警告的情况下执行此操作?
答案 0 :(得分:2)
此错误并非特定于路径相关类型。如果您尝试匹配任何Query[A]
,则会得到相同的错误,因为类型参数A
在运行时被删除。在这种情况下,类型参数不可能是您正在寻找的类型以外的任何其他参数。由于Query[A]
是QueryLike { type Result = A}
,它也应该是Query[query.Result]
,尽管这是一种看起来不太常见的方式。如果您愿意, 可以使用@unchecked
注释来取消警告:
def exec(query: QueryLike)(implicit cache: Cache): query.Result = query match {
case q: Query[query.Result @unchecked] => q.exec
case cq: CachedQuery[query.Result @unchecked] => cq.execWithCache(cache)
}
虽然很难说这是否适用于您的实际用例,但您也可以重构代码以避免完全匹配,并通过多态更优雅地处理它(可能)。由于上一个exec
无论如何都需要隐含的Cache
,因此对每个QueryLike
都允许这样做似乎没什么坏处。您的API可以通过这种方式更加统一,并且您无需确定要调用的方法。
trait Cache
trait QueryLike {
type Result
def exec(implicit cache: Cache): Result
}
trait Query[A] extends QueryLike {
type Result = A
}
trait CachedQuery[A] extends QueryLike {
type Result = A
}
def exec(query: QueryLike)(implicit cache: Cache): query.Result = query.exec
如果Query[A]
要求exec
没有Cache
,则您还可以提供DummyImplicit
的重载,以允许其在没有def exec(implicit d: DummyImplicit): Result
的情况下工作。
NSMangedObject
答案 1 :(得分:0)
实际上,问题似乎非常特定于路径依赖类型:问题是q和cq的类型。 q.Result是一个不兼容的查询类型。结果因为Scala类型检查器不知道,该查询和q和qc必须是相同的引用。
因此,Query [query.Result]实际上确实需要运行时类型检查。我注意到了这一点,当我尝试删除类型参数并只使用内部结果时。然后模式匹配不再生成警告,但q.exec的返回类型将与query.Result不兼容。
不使用@unchecked或asInstanceOf等的一个解决方案是将Result转换为QueryLike的类型参数。一般情况下,您不应该将抽象类型成员用于您想要“在上下文中”自由传递的内容。因此,在继承层次结构的某处将类型成员转换为类型参数有点奇怪。
所以这很好,正如编译器所知,他不必检查类型参数:
trait QueryLike[A] {
}
trait Query[A] extends QueryLike[A] {
def exec: A
}
trait CachedQuery[A] extends QueryLike[A] {
def execWithCache(cache: Cache): A
}
def exec[A](query: QueryLike[A])(implicit cache: Cache): A = query match {
case q: Query[A] => q.exec
case cq: CachedQuery[A] => cq.execWithCache(cache)
}
另一种方法是将方法添加到CachedQuery和Query的公共基本特征。
trait QueryLike {
type Result
}
trait Query[A] extends QueryLike with ExecWithCache {
type Result = A
def exec: Result
override def execWithCache(implicit cache: Cache) = exec
}
trait CachedQuery[A] extends QueryLike with ExecWithCache {
type Result = A
def exec(cache: Cache): Result
override def execWithCache(implicit cache: Cache) = exec(cache)
}
trait ExecWithCache extends QueryLike {
def execWithCache(implicit cache: Cache): Result
}
要改变这一点,Scala可能必须能够确定两个稳定存取器何时相同。