2015-06-03 2 views
1

Как написать функцию для преобразования String в общий тип (Double, Int, Float и т. Д.)?Scala generic cast from String function

Вот псевдокод для функциональности Я ищу:

def castFromString[A: Manifest](value: String): A = { 
    if (A == Double) { 
    parseDouble(value) 
    } else if (A == Int) { 
    parseInt(value) 
    } else { 
    value.toString() 
    } 
} 
+2

Я думаю, что вы имели в виду 'A: ClassTag' /' A: TypeTag', 'Manifest's являются устаревшими. Проверьте [это руководство] (http://docs.scala-lang.org/overviews/reflection/typetags-manifests.html). –

ответ

3

Я не думаю, что есть способ, чтобы написать функцию как таковую с Manifest, ClassTag или TypeTag как они могут только сказать вам тип во время выполнения. Чтобы что-то вроде функции псевдокода работало, возвращаемый тип должен быть известен во время компиляции.

Однако, можно реализовать функцию, указав type class и записывая экземпляры для типов, которые вы хотите отличить. Вот пример:

import java.lang.Double.parseDouble 
import java.lang.Integer.parseInt 

trait CastableFromString[A] { 
    def fromString(string: String): A 
} 

object CastableFromString { 
    implicit object DoubleCastableFromString extends CastableFromString[Double] { 
    override def fromString(string: String): Double = 
     parseDouble(string) 
    } 

    implicit object IntCastableFromString extends CastableFromString[Int] { 
    override def fromString(string: String): Int = 
     parseInt(string) 
    } 

    implicit object IntListCastableFromString extends CastableFromString[List[Int]] { 
    override def fromString(string: String): List[Int] = 
     string.split(',').map(parseInt).toList 
    } 

    def castFromString[A](string: String)(implicit cast: CastableFromString[A]): A = 
    cast.fromString(string) 
} 

object Main { 
    import CastableFromString._ 

    def main(args: Array[String]): Unit = { 
    val d = castFromString[Double]("4.5") 
    val i = castFromString[Int]("42") 
    val li = castFromString[List[Int]]("1,2,3") 
    println(s"d=$d i=$i li=$li") 
    } 
} 

В качестве альтернативы, чтобы приблизиться к чему-то, что напоминает ваш псевдокод, вы могли бы использовать macro. Вот пример (не будет работать ни на чем старше Scala 2.11):

import scala.language.experimental.macros 
import scala.reflect.macros.blackbox.Context 

class StringCasterImpl(val c: Context) { 
    def castFromStringImpl[A: c.WeakTypeTag](string: c.Expr[String]): c.Tree = { 
    import c.universe._ 
    val t = c.weakTypeOf[A] 
    if (t =:= typeOf[Double]) { 
     q"java.lang.Double.parseDouble($string)" 
    } else if (t =:= typeOf[Int]) { 
     q"java.lang.Integer.parseInt($string)" 
    } else if (t =:= typeOf[List[Int]]) { 
     q"$string.split(',').map(java.lang.Integer.parseInt).toList" 
    } else { 
     c.abort(c.enclosingPosition, s"Don't know how to cast $t to String") 
    } 
    } 
} 

object StringCaster { 
    def castFromString[A](string: String): A = 
    macro StringCasterImpl.castFromStringImpl[A] 
} 

// note that this object must be in a separate compilation unit 
object Main { 
    import StringCaster._ 

    def main(args: Array[String]): Unit = { 
    val d = castFromString[Double]("4.2") 
    val i = castFromString[Int]("42") 
    val li = castFromString[List[Int]]("1,2,3") 
    println(s"d=$d i=$i li=$li") 
    } 
} 
+0

Мне нравится scala, но это всего лишь один из тех нескольких «безумных разбитых окон», где java делает это намного лучше с помощью одного слова (вместо того, чтобы требовать иерархию объектов/классов). –

0

Вы можете определить неявные функции, которая преобразует строку определенного типа.

Затем, когда требуется, он автоматически будет заменен.

math.max(1,"3") // throws error 

но теперь

implicit def StringToInt(x: String): Int = x 

math.max(1,"3") //>> 3 
+1

Этот уровень имплицитов очень опасен имхо. –

0

Если вы хотите сделать это именно так, вы можете соответствовать runtimeClass в ClassTag:

def castFromString[A: ClassTag](value: String): A = { 
    val c = implicitly[ClassTag[A]].runtimeClass 
    if(c == classOf[Double]) parseDouble(value).asInstanceOf[A] 
    else ... 
} 

Примечание потребность в отливке asInstanceOf. Подход класса, предложенный @Olli, более безопасен и более идиоматичен.