[英]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
添加到ColumnInfo
來ColumnInfo
,例如[+T, U >: T]
,但這只給我帶來了其他錯誤。
非常感謝你的幫助 !
問題在於ColumnInfo[User]
不是ColumnInfo[Bean]
。 例如,如果您有
val myInfo = ColumnInfo[User]("MyCol", user => user.name)
val myBean = new Bean
myInfo.value(myBean)
這是不可能的,因為myBean
沒有name
方法(即使我們可以強制對其進行編譯,但它在運行時會失敗),因此編譯器將其捕獲並拋出。
實際上, ColumnInfo
在T
似乎是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}])
我們現在必須改變list
來cols.list
和columns
到cols.columns
在我們的模板匹配。
我們可以這樣稱呼我們的模板:
// In ListController...
Ok(views.html.list(TableData(users, columns)))
或者,我們可以解決這個問題。 給您的模板簽名:
@(list: List[Any], columns: List[ColumnInfo[Any]])
並在實際調用時將columns
為List[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.