更多关于scala的类型参数,尝试获得对类型的一致引用

时间:2012-09-09 09:06:27

标签: scala inheritance types covariance

好的,所以,我之前已经问过这件事了。理想情况下,我正在寻找一个通用的答案,这将有助于我理解如何一致地指定类型,但作为代替,我将解决如何解决具体问题。到目前为止,每个解决方案似乎带来了3个问题,我试图避免将整个应用程序放在这里,但我的目标是找到一种方法从任何地方以有用的方式引用递归参数化特征类型的类型,一个非平凡的程序,其特征类型的值可以互换使用。

所以,这里有更多示例代码:

//trait file, shouldn't need to know about implementing class.
trait MyTrait[T <: MyTrait[T]] { self:T =>
  val listOfT: List[T]
  def getFirst:T
  def getOne:T = if( !listOfT.isEmpty ) getFirst else self
}

case class Foo[A <: MyTrait[A]](i: MyTrait[A])

object MyTrait {
  def doSomething
      [T <: MyTrait[T], U[X <: MyTrait[X]] <: MyTrait[X]]
      (t: U[T]): T = t.getFirst

  def testMethod1
      [T <: MyTrait[T], U[X <: MyTrait[X]] <: MyTrait[X]]
      (something:Foo[T]):T=
              //error! type mismatch.  found:T, required: ?U[?T]
              doSomething(something.i.getOne)

  def testMethod2
       [T <: MyTrait[T], U[X <: MyTrait[X]] <: MyTrait[X]]
      (something:Foo[T]):T=
        //error! type mismatch.  
        // found: something.i.type (with underlying type this.MyTrait[T]
        //required: T
        something.i

  def testMethod3
       [T <: MyTrait[T], U[X <: MyTrait[X]] <: MyTrait[X]]
      (something:Foo[U[T]]):U[T]=
        //error: type arguments [U[T]] do not conform to class 
        //Foo's type parameter bounds [A <: this.MyTrait[A]]
        something.i.getOne


  // this works! ...but aren't something.i.getOne and something.i the same type?
  // why does testMethod2 fail if this works ?
  // what if I want to have a method that might return something.i and might return
  // soemthing.i.getOne?  What would the interface for that look like?
  def testMethod4
       [T <: MyTrait[T], U[X <: MyTrait[X]] <: MyTrait[X]]
      (something:Foo[T]):T=
        something.i.getOne

  def testMethod5
       [T <: MyTrait[T], U[X <: MyTrait[X]] <: MyTrait[X]]
      (something:Foo[U[T]]):U[T]=
        //error: type mismatch;
        //found: something.i.type (with underlying type this.MyTrait[U[T]]
        // required: U[T]
        something.i


}

//class file, shouldn't need to have MyTrait anywhere except 'extends' line.
//should be a usefull class on its own without adding the trait.
class MyClass extends MyTrait[MyClass] {
  //the point of using the parameterized type is to be able to return of 
  //List[MyClass] here instead of List[MyTrait] without having to override
  // some 'type' attribute in anything that uses the trait.
  override val listOfT: List[MyClass] = List[MyClass](this)
  override def getFirst: MyClass = listOfT.head
}


//some client code:
val mc = new MyClass
val foo = Foo(mc)
MyTrait.doSomething(foo.i)
//MyTrait.testMethod1(foo)

我想出了如何使用type参数:[T&lt;:MyTrait [T],U [X&lt ;:MyTrait [X]]&lt ;: MyTrait [X]] 从这个问题的答案: recursive type parameters in case class fields

我基本上也在问同样的事情,但更进一步地解决了这个问题。你可以在这里看到something.i基本上与something.i.getOne具有相同的类型,但是这些类型不能互换使用,因此这里的对象不能一直用作不同函数的参数。如何使这个代码以some.i和something.i.getOne(实际上甚至可能是同一个对象)具有与编译器和类型系统识别的相同类型的方式工作?

此特定问题的内容在示例代码的testMethod4中。

1 个答案:

答案 0 :(得分:1)

模式MyTrait[A <: MyTrait[A]]表示您希望直接使用A,而不再使用MyTrait,因为A扩展了此特征并保证返回自身的实例整个方法。

因此,错误是Foo的定义,它应该是简单的:

case class Foo[A <: MyTrait[A]](i: A)

FooMyClass编译和'客户代码'进行了更正。


此外,嵌套类似U[X <: MyTrait[X]] <: MyTrait[X]]的类型没有任何意义。最终,您将拥有一个表示类型。方法参数处于逆变位置,因此只需要具有T <: MyTrait[ T ]类型的参数就足够了,无论具体如何,您都可以使用任何表示类型。换句话说,Foo[U[T]]Foo[T]没有任何优势,但会使事情变得不必要地复杂化。在所有测试方法中,您基本上可以删除U类型参数。

object MyTrait {
  def doSomething[T <: MyTrait[T]](t: T): T = t.getFirst

  def testMethod1[T <: MyTrait[T]](something: Foo[T]): T =
    doSomething(something.i.getOne)

  def testMethod2[T <: MyTrait[T]](something: Foo[T]): T = something.i    
  def testMethod3[T <: MyTrait[T]](something: Foo[T]): T = something.i.getOne
  def testMethod4[T <: MyTrait[T]](something: Foo[T]): T = something.i.getOne
  def testMethod5[T <: MyTrait[T]](something: Foo[T]): T = something.i
}