当我尝试运行以下代码时:
def config[T](key: String): Option[T] = {
//in reality this is a map of various instance types as values
Some("string".asInstanceOf[T])
}
config("path").orNull
我遇到错误:
java.lang.String无法强制转换为scala.runtime.Null $ java.lang.ClassCastException
以下尝试正常进行:
config[String]("path").orNull
config("path").getOrElse("")
由于getOrElse
令人困惑,因此null为何如此特殊并引发错误。有没有一种方法orNull
可以不指定泛型类型?
scalaVersion:=“ 2.12.8”
答案 0 :(得分:1)
仅说明如何避免使用2 crf.fit(X_train, y_train)
~\Anaconda3\lib\site-packages\sklearn_crfsuite\estimator.py in fit(self, X, y, X_dev, y_dev)
312
313 for xseq, yseq in train_data:
--> 314 trainer.append(xseq, yseq)
315
316 if self.verbose:
pycrfsuite\_pycrfsuite.pyx in pycrfsuite._pycrfsuite.BaseTrainer.append()
pycrfsuite\_pycrfsuite.pyx in pycrfsuite._pycrfsuite.to_item()
pycrfsuite\_pycrfsuite.pyx in pycrfsuite._pycrfsuite.to_item()
TypeError: object of type 'generator' has no len()
从键入的配置中获取值。
asInstanceOf
然后,您可以像这样使用它。
sealed trait Value extends Product with Serializable
final case class IntValue(value: Int) extends Value
final case class StringValue(value: String) extends Value
final case class BooleanValue(value: Boolean) extends Value
type Config = Map[String, Value]
sealed trait ValueExtractor[T] {
def extract(config: Config)(fieldName: String): Option[T]
}
object ValueExtractor {
implicit final val IntExtractor: ValueExtractor[Int] =
new ValueExtractor[Int] {
override def extract(config: Config)(fieldName: String): Option[Int] =
config.get(fieldName).collect {
case IntValue(value) => value
}
}
implicit final val StringExtractor: ValueExtractor[String] =
new ValueExtractor[String] {
override def extract(config: Config)(fieldName: String): Option[String] =
config.get(fieldName).collect {
case StringValue(value) => value
}
}
implicit final val BooleanExtractor: ValueExtractor[Boolean] =
new ValueExtractor[Boolean] {
override def extract(config: Config)(fieldName: String): Option[Boolean] =
config.get(fieldName).collect {
case BooleanValue(value) => value
}
}
}
implicit class ConfigOps(val config: Config) extends AnyVal {
def getAs[T](fieldName: String)(default: => T)
(implicit extractor: ValueExtractor[T]): T =
extractor.extract(config)(fieldName).getOrElse(default)
}
现在,问题就变成了如何从原始源创建类型化的配置。
更好的是,如何直接将配置映射到 case类。
但是,这些操作更为复杂,最好使用已经完成的操作,例如pureconfig。
仅作为一项学术练习,让我们看看我们是否可以支持val config = Map("a" -> IntValue(10), "b" -> StringValue("Hey"), "d" -> BooleanValue(true))
config.getAs[Int](fieldName = "a")(default = 0) // res: Int = 10
config.getAs[Int](fieldName = "b")(default = 0) // res: Int = 0
config.getAs[Boolean](fieldName = "c")(default = false) // res: Boolean = false
和Lists
。
让我们从列表开始,一个幼稚的方法是为列表的值提供另一个case类,并为每种类型的列表创建一个提取器工厂(此过程正式称为隐式派生)。
Maps
现在,您可以像这样使用它。
import scala.reflect.ClassTag
final case class ListValue[T](value: List[T]) extends Value
...
// Note that, it has to be a def, since it is not only one implicit.
// But, rather a factory of implicits.
// Also note that, it needs another implicit parameter to construct the specific implicit.
// In this case, it needs a ClasTag for the inner type of the list to extract.
implicit final def listExtractor[T: ClassTag]: ValueExtractor[List[T]] =
new ValueExtractor[List[T]] {
override def extract(config: Config)(fieldName: String): Option[List[T]] =
config.get(fieldName).collect {
case ListValue(value) => value.collect {
// This works as a safe caster, which will remove all value that couldn't been casted.
case t: T => t
}
}
}
但是,如果您需要其他通用类型的列表(例如列表列表),则此方法仅限于普通类型。然后,这将无法工作。
val config = Map("l" ->ListValue(List(1, 2, 3)))
config.getAs[List[Int]](fieldName = "l")(default = List.empty)
// res: List[Int] = List(1, 2, 3)
config.getAs[List[String]](fieldName = "l")(default = List("Hey"))
// res: String = List() - The default is not used, since the field is a List...
// whose no element could be casted to String.
这里的问题是类型擦除, ClassTags 无法解决,您可以尝试使用 TypeTags 保留完整的类型,但是解决方案变得更加麻烦。
对于Maps,解决方案非常相似,尤其是如果您将键类型固定为val config = Map("l" ->ListValue(List(List(1, 2), List(3))))
val l = config.getAs[List[List[String]]](fieldName = "l")(default = List.empty)
// l: List[List[String]] = List(List(1, 2), List(3)) ???!!!
l.head
// res: List[String] = List(1, 2)
l.head.head
// java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
(假设您真正想要的是嵌套配置)。但是,这篇文章现在太长了,因此我将其留给读者练习。
但是,正如已经说过的那样,这很容易被打破,并且并不完全可靠。
有更好的方法,但是我自己对这些(还)并不熟练,即使我愿意,答案也会更长,而且根本没有必要。
幸运的是,即使 pureconfig 不直接支持 YAML ,也有module可以使用,pureconfig-yaml。
我建议您看一下该模块,如果还有其他问题,请提出一个新问题,直接标记 pureconfig 和 yaml 。另外,如果只是一个小疑问,您可以尝试在gitter channel中提问。