2015-08-11 1 views
6

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

object User { 
    case class Create(userName:String, firstName: String, lastName: String) 
    case class Created(userName:String, firstName: String, lastName: String) 
} 

object Group { 
    case class Create(groupName:String, members: Int) 
    case class Created(groupName:String, members: Int) 
} 

Учитывая этот вид установки, я устал от методов написания, которые принимают аргумент типа Создает и возвращает аргумент типа Created. У меня есть множество тестовых примеров, которые делают именно такие вещи.

Я мог бы написать функцию для преобразования одного класса case в другой. Эта функция преобразует User.Create в User.Created

def userCreated(create: User.Create) = User.Create.unapply(create).map((User.Created.apply _).tupled).getOrElse(sys.error(s"User creation failed: $create")) 

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

def transform[A,B](a: A):B 

Кроме того, эта функция не должна превзойти цель уменьшения шаблона. Пожалуйста, не стесняйтесь предлагать другую подпись для функции, если ее проще в использовании.

+0

Ожидаете ли вы что-то вроде 'convert (a) .to [B]'? –

+0

Важно ли, чтобы все эти парные классы имели имя create/created или ваше требование чисто, что они могут быть спарены и что одна функция преобразует первый класс любой такой пары во вторую? – itsbruce

ответ

5

Для записи, вот самый простой типизированного синтаксис я сумел реализовать:

implicit class Convert[A, RA](value: A)(implicit ga: Generic.Aux[A, RA]) { 
    def convertTo[B, RB](gb: Generic.Aux[B, RB])(implicit ev: RA =:= RB) = 
    gb.from(ga.to(value)) 
} 

И он может быть использован, как это:

case class Create(userName: String, firstName: String, lastName: String) 
case class Created(userName: String, firstName: String, lastName: String) 

val created = Create("foo", "bar", "baz").convertTo(Generic[Created]) 

Или то же самое с LabelledGeneric для обеспечения лучшего типа безопасности:

implicit class Convert[A, RA](value: A)(implicit ga: LabelledGeneric.Aux[A, RA]) { 
    def convertTo[B, RB](gb: LabelledGeneric.Aux[B, RB])(implicit ev: RA =:= RB) = 
    gb.from(ga.to(value)) 
} 

val created = Create("foo", "bar", "baz").convertTo(LabelledGeneric[Created])) 
10

Безшовный на помощь!

Вы можете использовать Shapeless's Generic для создания общих представлений классов case, которые затем могут использоваться для выполнения того, что вы пытаетесь сделать. Используя LabelledGeneric, мы можем применять оба типа и имена параметров.

import shapeless._ 

case class Create(userName: String, firstName: String, lastName: String) 
case class Created(userName: String, firstName: String, lastName: String) 
case class SortOfCreated(screenName: String, firstName: String, lastName: String) 

val c = Create("username", "firstname", "lastname") 

val createGen = LabelledGeneric[Create] 
val createdGen = LabelledGeneric[Created] 
val sortOfCreatedGen = LabelledGeneric[SortOfCreated] 

val created: Created = createdGen.from(createGen.to(c)) 

sortOfCreatedGen.from(createGen.to(c)) // fails to compile 
+0

Отличное решение! Однако существует возможная ошибка: поскольку «Generic» работает путем преобразования объектов в «HList», он не гарантирует соответствия соответствующих имен параметров исходного и целевого объектов. Таким образом, если у вас есть разные параметры с тем же типом или разными параметрами, вы получите неправильный результат преобразования во время выполнения. Там [много java utils] (http://stackoverflow.com/a/1432956/1349366), которые выполняют отображение путем отражения, которое решит эту проблему. – Aivean

+0

@Aivean Это твердая точка. Зависит от того, интересуетесь ли вы именами параметров или просто типами - это случай, когда классы значений будут сцеплены. – Ryan

+2

@Aivean Это может быть напрямую исправлено с помощью 'LabelledGeneric', для чего требуется точное соответствие имен и типов параметров классов классов и их порядка. – Kolmar

Смежные вопросы