簡體   English   中英

Scala-泛型,協變類型和函數作為參數

[英]Scala - Generics, covariant type and function as parameter

我正在嘗試從Play框架中的對象列表中構建一種<table>的通用方法。

我想創建一個表示列元數據的ColumnInfo類:

case class ColumnInfo[T](name: String, value: T => Any)  

name字段表示……好吧,列的名稱,函數value應在參數中包含一個對象,並為該列返回一個值。

假設我有一個User模型,擴展了另一個類(或特性,無論如何) Bean

case class User(name: String, age: Int) extends Bean  

然后,我創建一個Play Framework模板名稱list.scala.html ,它使用List[Bean]List[Column[Bean]]作為參數,並顯示相應的<table>

@(list: List[Bean], columns: List[ColumnInfo[Bean]])

<table>
    <thead>
        <tr>
        @for(c <- columns) {
            <th>@c.name</th>
        }
        </tr>
    </thead>
    <tbody>
    @for(obj <- list) {
        <tr>
        @for(c <- columns) {
            <td>@c.value(obj)</td>
        }
        </tr>
    }
    </tbody>
</table>

在控制器的Action中,我應該具有以下內容:

object ListController extends Controller {

  def list = Action {
    val users = List(
        User("foo", 20),
        User("bar", 30)
    )

    val columns = List(
        ColumnInfo[User]("Name", _.name),
        ColumnInfo[User]("Age", _.age)
    )

    Ok(views.html.list(users, columns)
  }
}

問題是我無法將ColumnInfo[User]放在ColumnInfo[Bean]的列表中!

那很正常 但是,如果我使ColumnInfo的類型T為協變量,它會告訴我:

case class ColumnInfo[+T](name: String, value: T => Any)

covariant type T occurs in contravariant position in type => (T) => Any of value value

邏輯。 但是我該怎么辦? 我還嘗試通過將其他類型U添加到ColumnInfoColumnInfo ,例如[+T, U >: T] ,但這只給我帶來了其他錯誤。

非常感謝你的幫助 !

問題在於ColumnInfo[User]不是ColumnInfo[Bean] 例如,如果您有

val myInfo = ColumnInfo[User]("MyCol", user => user.name)
val myBean = new Bean
myInfo.value(myBean)

這是不可能的,因為myBean沒有name方法(即使我們可以強制對其進行編譯,但它在運行時會失敗),因此編譯器將其捕獲並拋出。

實際上, ColumnInfoT似乎是ColumnInfo變的(出於示例中的原因,函數中的任何內容都是ColumnInfo變的-在某些語言中,他們實際上使用關鍵字in是協變的,這一點很清楚)。

因此,您可以將ColumnInfo定義ColumnInfo

case class ColumnInfo[-T](name: String, value: T => Any)

不幸的是,這限制了模板的重復使用,因為其簽名必須為@(list: List[User], columns: List[ColumnInfo[User]])

在理想情況下,模板將支持類型參數,例如常規的Scala方法,因此您可以使用@[T](list: List[T], columns: List[ColumnInfo[T]]) 但是, 播放模板當前不支持type參數

我可以看到兩種解決方法

存在類型

我們可以通過存在性類型來解決它。 我們將模板的參數包裝到不變的case類中:

case class TableData[T](list: List[T], columns: List[ColumnInfo[T]])

並將模板的簽名更改為:

@(cols: TableData[T forSome {type T}])

我們現在必須改變listcols.listcolumnscols.columns在我們的模板匹配。

我們可以這樣稱呼我們的模板:

// In ListController...
Ok(views.html.list(TableData(users, columns)))

演員表

或者,我們可以解決這個問題。 給您的模板簽名:

@(list: List[Any], columns: List[ColumnInfo[Any]])

並在實際調用時將columnsList[ColumnInfo[Any]]

// In ListController...
Ok(views.html.list(users, columns.asInstanceOf[List[ColumnInfo[Any]]]))

這將進行編譯,因為Scala使用類型擦除。 並且提供的list實際上是List[User] ,類型在運行時將是正確的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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