簡體   English   中英

使用類型參數def實現特征無法編譯

[英]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遮蔽,並且在該方法的上下文中對FooFoo實例的任何引用都是引用抽象類型參數Foo ,而不是具體類型Foo

  1. 您必須將特征案例類命名為彼​​此不同,因為兩種類型不能具有相同的名稱。 (Mike已經解釋過,為什么它在REPL中編譯)

  2. 在行def testFoo[Foo1](l: List[Foo1]) = {Foo1是一個新的泛型類型參數,而不是您的案例類 Foo1的類型。

  3. (恕我直言)用類型注釋隱式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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM