2017-04-12 182 views
1

有沒有辦法將一個案件類轉換爲另一種情況下,當他們有相同的領域,並從相同的特質繼承,而不提供轉換器功能(這隻會做一對一的字段映射)?斯卡拉案例類轉換

例如:

trait UberSomething { 
    val name: String 
} 
// these may be located in different files 
case class Something(name: String) extends UberSomething 
case class SomethingOther(name: String) extends UberSomething 

val s = Something("wtv") 
//s.asInstanceOf[SomethingOther] FAILS 
+1

無形絕對可以做到這一點,看看http://stackoverflow.com/questions/23192760/safely-copying-fields-between-case-classes-of-different-types –

+0

謝謝,這看起來很有希望 – Sofia

回答

3

首先從未定義trait成員val如果他們是爲了在以後得以實施點。

trait UberSomething { 
    def name: String 
} 
// these maybe in different files 
case class Something(name: String) extends UberSomething 
case class SomethingOther(name: String) extends UberSomething 

import shapeless._, ops.hlist.Align 

另一種方法我在什麼地方#2見過,所以道歉偷街頭信譽,是使用Align使得字段順序也沒有什麼關係。

class Convert[Target] { 
    def apply[Source, HLS <: HList, HLT <: HList](s: Source)(implicit 

    // Convert the Source to an HList type 
    // include field names, e.g "labelled" 
    // Shapeless "generates" this using an implicit macro 
    // it looks at our type, extracts a list of (Name, Type) pairs 
    genS: LabelledGeneric.Aux[Source, HLS], 

    // Convert the Target o an HList type 
    // include field names, e.g "labelled" 
    // So again we have a (Name, Type) list of pairs this time for Target 
    genT: LabelledGeneric.Aux[Target, HLT], 

    // Use an implicit align to make sure the two HLists 
    // contain the same set of (Name, Type) pairs in arbitrary order. 
    align: Align[HLS, HLT] 
) = genT from align(genS to s) 
} 
// Small trick to guarantee conversion only requires 
// a single type argument, otherwise we'd have to put something 
// in place for HLS and HLT, which are meant to be path dependant 
// and "calculated" by the LabelledGeneric.Repr macro so it wouldn't work as it breaches the "Aux pattern", which exposes a type member materialized by a macro in this case. 
// HLT and HLS come from within genS.Repr and genT.Repr. 
def convert[T] = new Convert[T] 

這是一個好一點的HList PARAMS是很好的掩蓋作爲apply一部分,所以你不要自己絆倒了。

val sample = Something("bla") 
convert[SomethingOther](sample) // SomethingOther("bla") 

我們來看看這一行:genT from align(genS to s)

  • 首先genS to sSource實例轉換爲LabelledGeneric,e.g一個HList與現場信息。

  • 對齊爲Source類型創建的HList的類型和字段以匹配Target類型。

  • genT from ..允許我們從授予的編譯器可以「證明」的字段和類型爲「所有有」一個HList,這是我們已經有了Align創建Target一個實例。

+0

這實際上並沒有在'gen2中編譯爲(gen from obj)' – Sofia

+0

另外,「從來沒有將特徵成員定義爲val,如果它們意味着以後再實施「。 - 你能詳細說明一下,還是指向相關鏈接?謝謝:) – Sofia

+1

@Sofia http://stackoverflow.com/questions/19642053/when-to-use-val-or-def-in-scala-traits。我已經更新了我的答案。 – flavian

1

你可以做,使用隱式轉換,如:

trait UberSomething { 
    val name: String 
} 

case class Something(name: String) extends UberSomething 
case class SomethingOther(name: String) extends UberSomething 

object Something { 
    implicit def somethingToSomethingOther(s:Something):SomethingOther = SomethingOther(s.name) 
} 
object SomethingOther { 
    implicit def somethingOtherToSomething(s:SomethingOther):Something = Something(s.name) 
} 

val s = Something("wtv") 
val so:SomethingOther = s 
+0

是的,我知道。創建這些轉換函數(隱式或不)是我試圖避免的。 – Sofia