2016-11-09 27 views
8

所以这是this Java question Scala的一个相当直接端口在斯卡拉避免重复泛型参数

我们有一堆采取通用的参数特性如下:

trait Ident { } 

trait Container[I <: Ident] { 
    def foo(id: I): String 
} 

trait Entity[C <: Container[I], I <: Ident] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
} 

这工作,但它是一个小因为在定义实体的子类时,我们必须提供Ident的类型和Container的类型。而事实上,就在容器的类型将是自身足够的类型信息:

class MyIdent extends Ident { } 
class MyContainer extends Container[MyIdent] { } 
class MyEntity extends Entity[MyContainer,MyIdent] { } 
//          ^^^^^^^ shouldn't really be necessary 

使用一个存在的类型避免了需要实体采取两个参数......当然你不能引用它稍后的。

trait Entity[C <: Container[I] forSome { type I <: Ident }] { 
    def container: C 
    def foo(id: I) = container.foo(id) 
//   ^^^ complains it has no idea what 'I' is here 
} 

同样将使用成员类型也不起作用的东西...

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 
    type C <: Container 
    def container: C 
    def foo(id: C#I) = container.foo(id) 
//         ^^ type mismatch 
} 

因此,没有人知道,如果有一个优雅的解决方案,以在斯卡拉这个问题呢?

+0

我不认为答案会与Java版本大不相同。没有一种真正的方法可以省略类型参数,而不会丢失随之而来的类型信息。 –

+0

是否可以将'container'设为'val'? – sepp2k

+0

@MichaelZajac所以在“MyEntity”的定义中,提供给Entity的第二个参数是多余的:除了“MyIdent”之外,根本没有其他可能的类型可用,实际上其他任何类型都会导致编译错误。当然,是否可以避免Scala中的冗余是另一个问题:-) –

回答

4

更新给出this answer我不知道这是否应该被认为是错误或不

你已经打SI-4377;如果你提供明确的type ascriptions你会得到我猜的错误只是暴露了类型预测是实现使用existentials:

trait Ident { } 

trait Container { 
    type I <: Ident 
    def foo(id: I): String 
} 

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = (container: C).foo(id: C#I) 
    // you will get something like: type mismatch; 
    // [error] found : Entity.this.C#I 
    // [error] required: _3.I where val _3: Entity.this.C 
    // as I said above, see https://issues.scala-lang.org/browse/SI-4377 
} 

这不是一个轻描淡写地说,这(错误?)让与泛型编程类型成员是一场噩梦。

虽然黑客,其中包括在铸造到手工制作的自我指涉的类型别名:

case object Container { 

    type is[C <: Container] = C with Container { 

    type I = C#I 
    // same for all other type members, if any 
    } 

    def is[C <: Container](c: C): is[C] = c.asInstanceOf[is[C]] 
} 

现在使用它,Entity编译:

trait Entity { 

    type C <: Container 
    def container: C 
    def foo(id: C#I): String = Container.is(container).foo(id) 
    // compiles! 
} 

这当然是危险的,根据经验,只有在C及其所有类型成员都绑定到非抽象类型时才是安全的它将被使用的地步;请注意,这并不总是如此,因为Scala让你留下“未定义”类型成员:

case object funnyContainer extends Container { 

    // I'm forced to implement `foo`, but *not* the `C` type member 
    def foo(id: I): String = "hi scalac!" 
} 
+1

谢谢,这个作品。很高兴知道我有正确的想法,但只是遇到了一个错误:-) –