好吧,我以前问过这个问题。理想情况下,我正在寻找一个通用的答案,它将帮助我理解如何一致地指定类型,但除此之外,我将解决如何解决特定问题。到目前为止,每个解决方案似乎又带来了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]从这个问题的答案来看:事例类字段中的递归类型参数
我基本上又问了一次同样的问题,但把问题再进一步。你可以在这里看到,something.i基本上与something.i.getOne具有相同的类型,但这些类型不能互换使用,因此对象不能一致地用作不同函数的参数。我如何让这些代码以一种方式工作,即something.I和something.I.getOne(实际上甚至可能是同一个对象)具有编译器和类型系统识别的相同类型?
这个特定问题的核心在示例代码中的testMethod4中。
模式MyTrait[A <: MyTrait[A]]
意味着您将希望直接使用A
,而不再使用MyTrait
,因为A
扩展了这一特性,并保证在整个方法中返回其自身的实例。
因此,错误在于Foo
的定义,它应该简单地为:
case class Foo[A <: MyTrait[A]](i: A)
对Foo
进行给定的更正后,您的MyClass
也会编译"客户端代码"。
此外,嵌套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
}