2015-11-06 27 views
3
trait Thing { 
    type Out 
    def get: Out 
} 

case class Wrapper(t: Thing) extends Thing { 
    type Out = t.Out 
    override def get = t.get 
} 

def hey(t: Thing): t.Out = Wrapper(t).get 

这给了我一个类型错误,虽然它显然是类型安全的。我想知道如何能够向编译器正确证明这是安全的,而无需执行演员。如何通过正确的包装类线程类型?

任何想法?

+0

使用'def hey(t:Thing):Thing#Out = Wrapper(t).get'适用于我 – GClaramunt

+0

这个编译,但它不比cast更好。>嘿(new Thing {type Out = Int; def get = 1}) res2:事情#出= 1 所以你没有得到一个有用的静态类型 –

回答

5

如果你真的,真的不希望把一个类型参数上Wrapper,您可以用较少的丢三落四apply推出自己的假案例类:

trait Thing { 
    type Out 
    def get: Out 
} 

abstract class Wrapper(t: Thing) extends Thing 

object Wrapper { 
    def apply(t: Thing): Wrapper { type Out = t.Out } = 
    new Wrapper(t) { 
     type Out = t.Out 
     def get: Out = t.get 
    } 
} 

def hey(t0: Thing): t0.Out = Wrapper(t0: Thing { type Out = t0.Out }).get 

(在现实生活中,你会还想要定义案例类给你的所有其他东西 - 有用的平等等)

问题是当定义一个case类时自动生成的Wrapper.apply只返回Wrapper,这意味着编译器已经全部丢失有关其Out的静态信息。如果您编写自己的apply,则可以通过将返回类型设置为指定Out的优化类型来保留该信息。

为了证明它的工作原理:

scala> val myThing = new Thing { 
    | type Out = String 
    | def get = "foo" 
    | } 
myThing: Thing{type Out = String} = [email protected] 

scala> hey(myThing) 
res0: myThing.Out = foo 

scala> val foo: String = hey(myThing) 
foo: String = foo 

所以编译器能够跟踪的事实OutString一路过关斩将。

+0

我看到...这很有道理 –

+0

一如既往,非常有帮助。 –

+0

非常好。包装的目的只是为了保存类型,对吗? – GClaramunt