[英]implementing a trait with a type parameter def fails to compile
我想我可能會做一些非常愚蠢的事情,但是下面的片段不能編譯。
scala> case class Foo1(i: Int)
defined class Foo1
scala> trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
defined trait Foo1
然后實現如下特征
scala> implicit val foodVal = new Foo1{
| def testFoo[Foo1](l:List[Foo1] ) = {
| for{
| a <- l
| if (a.i == 1)
| } yield a
| }
| }
<console>:13: error: value i is not a member of type parameter Foo1
更新感謝您的回復。 我真的很想知道在trait
級別和def
級別之間傳遞類型之間的區別。 在答案中,我可以看到類型是在一個特征級別傳遞,它確實有效,但后面仍然給我相同的錯誤
scala> trait Bar { def testFoo[T](l1: List[T] ) : List[T] }
defined trait Bar
scala> implicit val fooVal = new Bar {
| override def testFoo[Foo1](l:List[Foo1]) : List[Foo1] = {
| for { a <- l if a.i == 1} yield a
| }
| }
<console>:18: error: value i is not a member of type parameter Foo1
在REPL,定義:
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
正在取代類定義
case class Foo1(i: Int)
如果您嘗試使用paste命令執行相同的操作,您將看到兩者無法一起定義:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo1(i: Int)
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
// Exiting paste mode, now interpreting.
<console>:36: error: Foo1 is already defined as case class Foo1
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
^
因此,您需要為您的特征使用與您的案例類不同的名稱,這是明智的,因為它們指的是不同的東西。
重命名后,有幾種方法可以實現您的目標。 一個由Sascha概述(使用類型成員),或者您可以提升類型T
來參數化您的特征,而不是在特征上參數化方法。
case class Foo1(i: Int)
trait Bar[T] {
def testFoo(l1 : List[T] ) : List[T]
}
implicit val foodVal: Bar[Foo1] = new Bar[Foo1] {
override def testFoo(l: List[Foo1]): List[Foo1] = {
for { a <- l if a.i == 1 } yield a
}
}
要回答更新問題,答案是聲明和實現之間的區別。
當我在下面的表單中編寫參數化特征的實現時,我說“用具體類型Foo
替換抽象類型T
”。
trait Bar[T] {...}
val instance = new Foo[Bar] { ... }
但是當我覆蓋一個按類型參數化並更改類型名稱的方法時,我所做的就是為該抽象類型指定一個不同的名稱。 我沒有指定要使用的具體具體類,我正在指定一個新符號來標識我正在操作的抽象類型。
trait Bar {
def something[T](t: T) : T
}
val instance = new Bar {
def something[Foo](t: Foo) : Foo = { ... }
}
因此,在上面的示例中, Foo
不是具體類型Foo
,而Foo
只是一個符號,表示將根據傳遞給方法的參數類型具體化的任意類型。
因此,即使在同一范圍內存在一個名為Foo
的具體類型,它也會被引入的類型參數Foo
遮蔽,並且在該方法的上下文中對Foo
或Foo
實例的任何引用都是引用抽象類型參數Foo
,而不是具體類型Foo
。
您必須將特征和案例類命名為彼此不同,因為兩種類型不能具有相同的名稱。 (Mike已經解釋過,為什么它在REPL中編譯)
在行def testFoo[Foo1](l: List[Foo1]) = {
, Foo1
是一個新的泛型類型參數,而不是您的案例類 Foo1
的類型。
(恕我直言)用類型注釋隱式val總是一個好主意。
也就是說,這里是應該做的代碼,你想要的:
case class Foo1(i: Int)
trait Foo2 {
type T
def testFoo(l1 : List[T] ) : List[T]
}
implicit val foodVal: Foo2 = new Foo2 {
type T = Foo1
def testFoo(l: List[T]) = {
for { a <- l if a.i == 1 } yield a
}
}
更新(回答問題更新):
類型參數,尤其是命名參數,與常用參數非常相似。 是否訪問類參數或具有相同名稱的函數參數的關鍵是范圍 。
class Foo(arg: Int) {
val field: Int = 1
def func(field: String) = {
// here field will always refer to the function paramter
// if you want to access the class field you would have to call this.field
}
}
class Bar[T] {
def func[T](t: T) = {
// within the scope of the function T IS NOT "Bar#T".
// However, you cannot access the class type parameter with "this.T" in this example
}
def func2(t: T) = {
// here T is the classes type parameter T
}
}
要通過this.
完成訪問示例this.
class Bar[T] {
type Inner = T
def func[Inner](t: Inner) = {
// won't compile because the "Inner" type of the class is initialized with T
val test: this.Inner = t
}
}
如果你沒有進一步指定一個類型參數,它可以是Any
(thing)。 如果您需要具有特定API的類型。 您需要使用<:
運算符告訴編譯器。
trait Api {
def func: String
}
def func[T <: Api](arg: T) = {
arg.func // yay
}
這稱為上限類型 。 有許多任意復雜的方法來細化類型參數,您必須閱讀文檔。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.