Классы дел - Скопируйте несколько полей с помощью преобразований - PullRequest
2 голосов
/ 27 мая 2019

У меня есть класс дел с 25 полями, и мне нужно преобразовать его в другой с 22 полями, из которых 19 из них являются общими, а 3 просто переименованы.

Я нашел несколько примеров того, как это сделать, используя shapeless (например, ответ здесь и несколько примеров кода из Miles Sabin здесь и здесь ), но последний из них выглядит несколько устаревшим, и я не могу понять из примера на Github, как я могу использовать бесформенный для переименования нескольких полей или сделать больше манипуляций над полем перед добавлением его вновый объект.Кто-нибудь может мне помочь?

Пример упрощенного кода;

import shapeless.LabelledGeneric
case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)

val aGen = LabelledGeneric[A]
val bGen = LabelledGeneric[B]

val freddie = new A(1,"Freddie","somestring")

val record = aGen.to(freddie)
val atmp = freddie.fieldA
record.Remove("fielda")

val freddieB = bGen.from(record + 
  (Symbol("fieldARenamed") ->> atmp) +
  (Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase)
) //Errors everywhere, even if I replace + with :: etc.

У меня есть ощущение, что где-то здесь появится 1015 *, но понимание того, как это сделать вбыл бы интересен самый простой способ - например, без создания дополнительных черт, таких как Field, как в третьей ссылке выше.

В Руководство по бесформенным формам , также возможно использование одногоцитата, (например, 'fieldC) нотация, о которой я не смог найти много информации, поэтому, если это играет роль, некоторые объяснения также были бы действительно полезны.Совершенно новый для этой глубины колдовства Scala, поэтому извиняюсь, если вопрос кажется тупым или охватывает слишком много разнородных тем.

РЕДАКТИРОВАТЬ: Во избежание сомнений, я не ищет ответы, которыепредлагаю мне просто вручную создать новый класс дел, ссылаясь на поля из первого, как в;

val freddieB = B(fieldARenamed = freddie.fieldA, fieldB = freddie.fieldB, fieldC = freddie.fieldC, fieldCTransformed =freddie.fieldC.toUpperCase)

См. комментарий ниже по различным причинам, почему это неуместно.

Ответы [ 3 ]

2 голосов
/ 27 мая 2019

Еще один вариант - использовать automapper ; в частности, функция динамического сопоставления .

Для вашего конкретного примера это будет выглядеть следующим образом:

import io.bfil.automapper._

case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)

val freddie = new A(1,"Freddie","somestring")

val freddieB = automap(freddie).dynamicallyTo[B](
  fieldARenamed = freddie.fieldA, 
  fieldCTransformed = freddie.fieldC.toUpperCase
)

и я думаю, вы можете сделать это функцией

def atob(a: A): B = {
  automap(a).dynamicallyTo[B](
    fieldARenamed = a.fieldA, 
    fieldCTransformed = a.fieldC.toUpperCase
  )
}

С точки зрения эффективности, эта библиотека использует макросы, поэтому сгенерированный код практически так же хорош, как можно было бы написать вручную

2 голосов
/ 27 мая 2019

Самое простое решение - создать экземпляр нового case class, используя значения из старого, применяя функции к значениям по мере необходимости.Код будет очень эффективным, цель кода будет очень ясной, на его написание потребуется меньше времени, чем на любое другое решение, он будет более надежным и поддерживаемым, чем решение, зависящее от сторонних библиотек, и избегаетскрытая зависимость между двумя классами.

1 голос
/ 31 мая 2019

Только к вашему сведению, вот один из способов заставить ваш код вопроса работать.

import shapeless._
import shapeless.labelled.FieldType
import shapeless.ops.hlist.{Align,Intersection}
import shapeless.syntax.singleton._

case class A(fieldA:Int, fieldB:String, fieldC:String)
case class B(fieldARenamed:Int, fieldB:String, fieldC:String, fieldCTransformed:String)

val fromGen = LabelledGeneric[A]
val toGen   = LabelledGeneric[B]

val freddie = A(1, "Freddie", "somestring")
val putARename = Symbol("fieldARenamed")     ->> freddie.fieldA
val putCTrans  = Symbol("fieldCTransformed") ->> freddie.fieldC.toUpperCase

trait Field { type K; type V; type F = FieldType[K, V] }
object Field {
  def apply[K0,V0](sample: FieldType[K0,V0]) =
    new Field { type K = K0; type V = V0 }
}

val pFieldA  = Field(putARename)
val pFieldCT = Field(putCTrans)

val inter = Intersection[pFieldA.F :: pFieldCT.F :: fromGen.Repr, toGen.Repr]
val align = Align[inter.Out, toGen.Repr]

toGen.from(align(inter(putARename :: putCTrans :: fromGen.to(freddie))))
//res0: B = B(1,Freddie,somestring,SOMESTRING)
...