我是否误解了Scala中的implciits如何工作 - 考虑到Scala中的以下特征,
trait hasConfig {
implicit def string2array(s: java.lang.String): Array[String] = {
LoadedProperties.getList(s)
}
implicit def string2boolean(s: java.lang.String) : java.lang.Boolean = {
s.toLowerCase() match {
case "true" => true
case "false" => false
}
}
var config: Properties = new Properties()
def getConfigs : Properties = config
def loadConfigs(prop:Properties) : Properties = {
config = prop
config
}
def getConfigAs[T](key:String):T = {
if (hasConfig(key)) {
val value : T = config.getProperty(key).asInstanceOf[T]
value
}
else throw new Exception("Key not found in config")
}
def hasConfig(key: String): Boolean = {
config.containsKey(k)
}
}
虽然java.util.properties包含(String,String)键值对,但由于定义了隐式转换,我希望以下代码能够正常工作,
class hasConfigTest extends FunSuite {
val recModel = new Object with hasConfig
//val prop = LoadedProperties.fromFile("test") Read properties from some file
recModel.loadConfigs(prop)
test("test string paramater") {
assert(recModel.getConfigAs[String]("application.id").equals("framework"))
}
test("test boolean paramater") {
assert(recModel.getConfigAs[Boolean]("framework.booleanvalue") == true)
//Property file contains framework.booleanvalue=true
//expected to return java.lang.boolean, get java.lang.string
}
}
但是,我收到以下错误,
java.lang.String cannot be cast to java.lang.Boolean
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Boolean
为什么implcit转换没有解决这个问题?
答案 0 :(得分:3)
它不起作用,因为强制转换(asInstanceOf
)与隐式转换完全不同。有多种方法可以解决这个问题。
如果您想使用硬核隐式转换魔术,您应该像这样重写getConfigAs
方法:
def getConfig(key:String): String = {
if (hasConfig(key)) {
val value: String = config.getProperty(key)
value
}
else throw new Exception("Key not found in config")
}
使用getConfig
时,您必须将转化内容导入当前范围。
val recModel = new Object with hasConfig
import recModel._
recModel.loadConfigs(prop)
val value: Boolean = recModel.getConfig("framework.booleanvalue")
更好的方法是保留当前的API,但之后您必须引入隐式参数,因为getConfigAs
的实现需要访问转换。
def getConfigAs[T](key:String)(implicit conv: String => T): T = {
if (hasConfig(key)) {
val value: String = config.getProperty(key)
value
}
else throw new Exception("Key not found in config")
}
您仍然需要在使用网站上导入必要的转换。
val recModel = new Object with hasConfig
import recModel._
recModel.loadConfigs(prop)
val value = recModel.getConfigAs[Boolean]("framework.booleanvalue")
避免必须导入转化(并且可能意外隐式转换各种Strings
)的方法是引入一种新类型来对转化进行编码。然后,您可以在其伴随对象中实现转换,隐式搜索可以在不导入它们的情况下找到它们。
trait Converter[To]{
def convert(s: String): To
}
object Converter {
implicit val string2array: Converter[Array[String]] = new Converter[Array[String]] {
def convert(s: String): Array[String] =
LoadedProperties.getList(s)
}
implicit val string2boolean: Converter[Boolean] = new Converter[Boolean] {
def convert(s: String): Boolean =
s.toLowerCase() match {
case "true" => true
case "false" => false
}
}
}
然后您可以更改getConfigAs
方法。
def getConfigAs[T](key:String)(implicit conv: Converter[T]): T = {
if (hasConfig(key)) {
val value: String = config.getProperty(key)
conv.convert(value)
}
else throw new Exception("Key not found in config")
}
并使用它。
val recModel = new Object with hasConfig
recModel.loadConfigs(prop)
val value = recModel.getConfigAs[Boolean]("framework.booleanvalue")
您可能还想查看here。
答案 1 :(得分:1)
隐式转换应在范围内定义,例如在封闭对象中或导入到当前范围中。在您的情况下,它们应该在hasConfigTest
类的范围内定义。
http://docs.scala-lang.org/tutorials/FAQ/finding-implicits
这是一个简单的可重复示例:
object m {
implicit def string2boolean(s: String): Boolean = {
s.toLowerCase() match {
case "true" => true
case "false" => false
}
} //> string2boolean: (s: String)Boolean
println(false || "true") //> true
println(false || "false") //> false
}
答案 2 :(得分:1)
我认为你想说的是这样的:
import java.util.Properties
object LoadedProperties {
def getList(s: String): Array[String] = Array.empty
}
object hasConfig {
sealed trait ConfigReader[T] {
def read(conf: String): T
}
implicit object BooleanConfigReader extends ConfigReader[Boolean] {
override def read(conf: String): Boolean = conf.toLowerCase() match {
case "true" => true
case "false" => false
}
}
implicit object ArrayConfigReader extends ConfigReader[Array[String]] {
override def read(s: String): Array[String] = {
LoadedProperties.getList(s)
}
}
var config: Properties = new Properties()
def getConfigs: Properties = config
def loadConfigs(prop: Properties): Properties = {
config = prop
config
}
def getConfigAs[T](key: String)(implicit reader: ConfigReader[T]): T = {
val prop = config.getProperty(key)
if (prop == null)
throw new Exception("Key not found in config")
reader.read(prop)
}
}
val props = new Properties()
props.setProperty("a", "false")
props.setProperty("b", "some")
hasConfig.loadConfigs(props)
hasConfig.getConfigAs[Boolean]("a")
hasConfig.getConfigAs[Array[String]]("a")