在Scala中初始化和使用抽象泛型类中的字段

时间:2012-04-23 19:24:47

标签: scala generics abstract

我在scala中有这样的东西:

abstract class Point[Type](n: String){
    val name = n
    var value: Type = _
}

到目前为止一切顺利。问题出现在扩展Point的类中。

case class Input[Type](n:String) extends Point(n){
    def setValue(va: Type) = value = va
}

setValue行,我遇到了这个问题:

[error]  type mismatch;
[error]  found   : va.type (with underlying type Type)
[error]  required: Nothing
[error]   def setValue(va: Type) = value = va

我尝试使用nullnull.asInstanceOf[Type]初始化,但结果是一样的。

如何初始化值,以便可以在setValue中使用?

2 个答案:

答案 0 :(得分:7)

您应该使用通用类型Input指定Point实现Type,因为现在,由于未指定,它被视为Nothing(我猜是编译器无法从setValue方法推断出它。所以你必须做以下事情:

case class Input[Type](n:String) extends Point[Type](n){
  def setValue(va: Type) = value = va
}

更多信息

我回答了这个问题的编译错误(它在scala 2.9.0.1上编译)。此外,我将此案例类视为现有类型的实现,如“Int”。在抽象类中使用_当然是一个坏主意,但是它不是禁止的,但是_并不总是为空,它是默认值,例如:var x:Int = _将赋值{{ 1}}到0

答案 1 :(得分:1)

尝试以下方法:

包inputabstraction

abstract class Point[T](n:String){
  def value: T
  val name = n
}

case class Input[T](n:String, value:T) extends Point[T](n)

object testTypedCaseClass{
  def test(){
    val foo = Input("foo", "bar")
    println(foo)
  }
}

一个简单的应用程序来检查它是否有效:

import inputabstraction._


object TestApp extends Application{
  testTypedCaseClass.test()  
}

<强>解释

您犯的第一个错误是case class Input[Type](n:String) extends Point(n){。 Point是一个类型化的类,因此当您使用extends Point(n)调用超类构造函数时,需要指定Point的类型。这样做:extends Point[T](n),其中T是您计划使用的类型。

第二个错误是您要定义和声明值:T:var value: Type = _。在此声明中,_是一个值。它的值是Nothing。 scala编译器推断出Point[T]Point[Nothing]。因此,当您尝试将其设置为setValue方法正文中的类型时,必须将其设置为Nothing,这可能不是您想要的。如果您尝试将其设置为Nothing以外的任何内容,则会从上面获得类型不匹配,因为由于您使用了Nothing,因此值被输入为_

第三个错误是使用var代替valdefvaldef可以互换,这意味着子类型可以覆盖valdef,scala编译器会为您解决。最好的做法是在抽象类和特征中使用def将val定义为函数,因为子类型构造函数的初始化顺序是一个非常困难的事情(编译器决定如何algorithm如何从其超类型构造一个类)。 TL#DR ===在超类型中使用def。案例类参数会自动生成val个字段,由于您要扩展一个Point,因此会创建一个覆盖Point[T]中def值字段的val值字段。

由于类型推断和Type是抽象的事实,你可以放弃Scala中的所有T || Point抽象,因此可以通过val扩展值。

这样做依赖注入的首选方法是cake pattern,但是这个例子我已经为你的用例提供了工作。