2016-10-04 5 views
1

Мне нужно проверить целостность вложенных схем и, следовательно, писать классы case для этого. Основное препятствие, с которым я столкнулся, это схема, которая может иметь поле (скажем, name) либо из String, либо типа Utf8, и я хочу принять оба экземпляра. Можно ли избежать два класса регистра, какКласс случая Scala с аргументом нескольких типов

case class NameValueString(name: String, value: Double) 
case class NameValueUtf8(name: Utf8, value: Double) 

и что-то вроде

case class NameValue(name @(_:String | _:Utf8), value: Double) 

Приведенное выше выражение, конечно, не может компиляции.

Нихилу

+0

Я хотел бы создать новый конструктор корпус класса NameValueString (имя: String, значение: Double) { защиту это (имя: UTF8, значение: Double) = это (name.toString, значение) } это действительно для вас? – gasparms

ответ

3

Один подход так называемых классов типа:

trait StringLike[A] // sealed if you don't want anybody to implement it elsewhere 
object StringLike { 
    implicit object StringEv extends StringLike[String] {} 
    implicit object Utf8Ev extends StringLike[Utf8] {} 
} 

case class NameValue[A](name: A, value: Double)(implicit val stringLike: StringLike[A]) 

Конечно, StringLike обычно не может быть пустым, но описать все, что общие функции, которые вам нужно от обоих String и Utf8.

Вы можете соответствовать на свидетельствах:

def nameLength[A](nameValue: NameValue[A]) = nameValue.stringLike match { 
    case StringLike.StringEv => 
    nameValue.name.length // calls String#length 
    case StringLike.Utf8Ev => 
    nameValue.name.length // calls Utf8#length (assuming Utf8 has such method) 
} 

В этом случае компилятор будет даже знать, что A (и поэтому тип nameValue.name) является String в первой ветви и Utf8 во втором.

+0

Хороший ответ.Я хотел бы добавить, что вы можете использовать синтаксис «связанный контекст» для достижения того же результата: 'case class NameValue [A: StringLike] (имя: A, значение: Double)' – stefanobaghino

+0

'implicit val utf8: StringLike [ Utf8] = new StringLike [Utf8] {} 'Я полагаю? –

+0

Спасибо за быстрый ответ. Я пробую ваше предложение. Только один вопрос, как выглядит запрос соответствия шаблону? 'case x: NameValue => ...' не записывает компилятору без параметра template/type :(. –

1

Другой образец (не требует неявных аргументов):

import scala.language.implicitConversions 

class StringLike[A](name: A) { 
    override def toString = { 
    name match { 
     case s: String => s"String: $s" 
     case i: Int => s"Int: $i" 
    } 
    } 
} 
implicit def string2StringLike(s: String) = new StringLike(s) 
implicit def int2StringLike(i: Int) = new StringLike(i) 

case class NameValue[A](name: StringLike[A], value: String) { 
    override def toString = name.toString 
} 

NameValue("123", "123") 
//> NameValue[String] = String: 123 
NameValue(13, "123") 
//> NameValue[Int] = Int: 13 
NameValue(13.9, "123") 
// error: type mismatch; 
// found : Double(13.9) 
// required: StringLike[?] 
//  NameValue(13.9, "123") 
//    ^

UPDATE

Вот как я вижу завершения класса типа подход, основанный на ответе Алексея:

trait StringLike[A] { 
    def toString(x: A): String 
} 

object StringLike { 
    implicit object StringStringLike extends StringLike[String] { 
    def toString(s: String) = s"String: $s" 
    } 
    implicit object IntStringLike extends StringLike[Int] { 
    def toString(i: Int) = s"Int: $i" 
    } 
} 

import StringLike._ 

case class NameValue[A](name: A, value: Double)(implicit ev: StringLike[A]) { 
    override def toString = ev.toString(name) 
} 

NameValue(1, 2.0) 
//> NameValue[Int] = Int: 1 

NameValue("123", 2.0) 
//> NameValue[String] = String: 123 

NameValue(2.0, 2.0) 
// error: could not find implicit value for parameter ev: 
// StringLike[Double] 
//  NameValue(2.0, 2.0) 
//    ^

UPDATE2

Еще один (с использованием union type для безопасности типа):

type ¬[A] = A => Nothing 
type ¬¬[A] = ¬[¬[A]] 
type ∨[T, U] = ¬[¬[T] with ¬[U]] 
type |∨|[T, U] = { type λ[X] = ¬¬[X] <:< (T ∨ U) } 

def nameLength[A: ClassTag: (Int |∨| String)#λ](nameValue: NameValue[A]) = 
    nameValue.name match { 
    case s:String => s.length 
    case i:Int => i + 1 
    } 
0

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

case class NameValue(name: String, value: Double) 
object NameValue{ 
    def apply(name: Utf8, value: Double): NameValue = { 
     new NameValue(name.toString, value) 
    } 
} 

В качестве альтернативы, если вы хотите, чтобы соответствовать шаблону и извлечь из различных имя-значение параметров, то вам может понадобиться проверить экстракторов, который в основном создают свои собственные методы ... Исключить проверить http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html