0

好的,所以,我以前问过这个问题。理想情况下,我正在寻找一个通用答案,以帮助我理解如何一致地指定类型,但取而代之的是,我将解决如何解决特定问题。到目前为止,每个解决方案似乎又带来了 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)

我从这个问题的答案中弄清楚了如何使用类型参数: [T <: MyTrait[T], U[X <: MyTrait[X]] <: MyTrait[X]]: recursive type parameters in case class fields

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

这个特定问题的重点在示例代码中的 testMethod4 中。

4

1 回答 1

1

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

因此,错误在于 的定义Foo,应该很简单:

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

通过给定的更正Foo,您的MyClass编译和“客户端代码”也是如此。


此外,嵌套类型 likeU[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
}
于 2012-09-09T10:00:41.293 回答