scala> case class Data[+T](val value:T=null)
defined class Data
scala> val foo=Data[ArrayBuffer[Data[Any]]]()
foo: Data[scala.collection.mutable.ArrayBuffer[Data[Any]]] = Data(null)
scala> foo.value+=Data[String]()
java.lang.NullPointerException
... 33 elided
我想要一个Data类,它实例化为Data [String],Data [ArrayBuffer [Data [Any]]]或Data [Map [String,Data [Any]]]。在上面的例子中,我尝试将其实例化为Data [ArrayBuffer [Data [Any]]]并将Data [String]添加到其arraybuffer中。当然我得到一个空指针异常,因为value为null。但这个例子的重点是它至少可以编译并运行。
现在,在Data构造函数中,我想将value实例化为Data [String],ArrayBuffer [Data [Any]]或Map [String,Data [Any]],具体取决于最初为null的值的类型由getClass方法返回。但是为此,我需要将值变为var,以便在检查其null值的类型后对其进行修改。
但是我收到了这个错误:
scala> case class Data[+T](var value:T=null)
<console>:11: error: covariant type T occurs in contravariant position in type T of value value_=
case class Data[+T](var value:T=null)
答案 0 :(得分:4)
在Data
中设置T
不变量。只需删除+
:Data[T]
- 这应该编译。
更好的是,重新考虑你的设计以摆脱空值和可变变量 - 它们都闻到了。
修改:阅读完评论后,我会更清楚您的目标。考虑这样的事情,例如作为选项之一。
sealed trait Node
case class ListNode(list: Seq[Node]) extends Node
case class MapNode(map: Map[String, Node]) extends Node
case class LeafNode(data: String) extends Node
现在你可以用类似的东西解析你的文档(这是“伪代码”,将它调整为你正在使用的任何xml解析库):
def parseNode(tag: XMLTag): Node = tag.tagType match {
case LIST =>
val subNodes = tag.getNestedTags.map(parseNode)
ListNode(subNodes)
case MAP =>
val subNodes = tag.getNestedTags.map { tag =>
tag.name -> parseNode(tag)
}
MapNode(subNodes.toMap)
case _ =>
LeafNode(tag.text)
}
答案 1 :(得分:3)
http://like-a-boss.net/2012/09/17/variance-in-scala.html#variance_and_type_safety
方差和类型安全
使用var字段定义泛型类时,我们可能会遇到编译时错误:
scala> class Invariant[T](var t: T)
defined class Invariant
scala> class Covariant[+T](var t: T)
<console>:7: error: covariant type T occurs in contravariant position in type T of value t_=
class Covariant[+T](var t: T)
^
scala> class Contravariant[-T](var t: T)
<console>:7: error: contravariant type T occurs in covariant position in type => T of method t
class Contravariant[-T](var t: T)
让我们分解一下。为什么编译器不允许在Covariant类中使用getter?
scala> abstract trait Covariant[+T] {
| def take(t: T): Unit
| }
<console>:8: error: covariant type T occurs in contravariant position in type T of value t
def take(t: T): Unit
^
scala> abstract trait Contravariant[-T] {
| def take(t: T): Unit
| }
defined trait Contravariant
为什么呢?让我们考虑一下协方差的用法,假设我们有一个类:
class Printer[+T] {
| def print(t: T): Unit = ???
| }
<console>:8: error: covariant type T occurs in contravariant position in type T of value t
def print(t: T): Unit = ???
如果打印方法可以打印狗是否有意义(一般情况下)它还应该打印动物?也许有时候但是在一般意义上,如果我们想要概括Printer类,我们应该使用逆变。编译器非常聪明,可以为我们检查这种用法。
让我们考虑第二个用例:返回一个通用参数:
scala> class Create[-T] {
| def create: T = ???
| }
<console>:8: error: contravariant type T occurs in covariant position in type => T of method create
def create: T = ???
再一次 - Create是否应该通过逆变来概括?如果Create返回Animal类的实例,我们是否应该能够在每个需要Create [Dog]的地方使用它? scala编译器足够智能,如果我们尝试它就会在我们面前爆炸。
答案 2 :(得分:0)
我可以这样做:
package data
case class Data[+T](val value:T)
{
println(value.getClass)
}
这样我必须从构造函数中显式初始化值。没错,只是我发现它有点过于冗长。
import data.Data
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Map
object Run extends App
{
val a=Data[ArrayBuffer[Data[Any]]](ArrayBuffer[Data[Any]]())
a.value+=Data[String]("bar")
println(a.value)
val m=Data[Map[String,Data[Any]]](Map[String,Data[Any]]())
m.value+=("foo"->Data[String]("bar"))
println(m.value)
}
打印:
class scala.collection.mutable.ArrayBuffer
class java.lang.String
ArrayBuffer(Data(bar))
class scala.collection.mutable.HashMap
class java.lang.String
Map(foo -> Data(bar))
程序只编译带有+ T类型参数的数据,否则我得到错误:
type mismatch;
[error] found : data.Data[String]
[error] required: data.Data[Any]
[error] Note: String <: Any, but class Data is invariant in type T.
[error] You may wish to define T as +T instead. (SLS 4.5)
[error] a.value+=Data[String]("bar")