在scala中使用类型参数

时间:2013-04-17 20:22:56

标签: scala

我正在尝试将一堆字符串转换为通用堆栈。

这是Stack of String实现:

class LinkedStackOfStrings {

  var first : Node = _

  def isEmpty : Boolean  = {
    first == null
  }

  def push(itemName : String) = {
    val oldFirst = first
    first = new Node(itemName , oldFirst)
  } 

  def pop = {
    first = first.next

    first.itemName

  }

}

  class Node(val itemName : String , val next : Node) {}

这是我的Stack of Generic类型实现:

class LinkedStackGeneric[T] {

  var first : NodeGeneric[T] = _

  def isEmpty : Boolean  = {
    first == null
  }

  def push(itemName : T) = {
    val oldFirst = first
    first = new NodeGeneric(itemName , oldFirst)
  } 

  def pop = {
    first = first.next

    first.itemName

  }

}

  class NodeGeneric[T](val itemName : T , val next : NodeGeneric[T]) {}

当我尝试使用数据初始化我的新泛型类时:

   val generic = new LinkedStackGeneric
   generic.push("test")

我收到此语法错误:

type mismatch; found : String("test") required: Nothing

我做错了什么?由于我使用的是类型参数,我不能将任何类型的数据(包括String)添加到方法'push'中吗?

3 个答案:

答案 0 :(得分:4)

乍一看,我不指望你想要做什么。

在实例化对象时,类型推断没有基础来推断类型。在下一行中你推动String是不够聪明的。因此,您必须这样做:

val generic = new LinkedStackGeneric[String]
generic.push("test")

我很想被证明是错的。

答案 1 :(得分:3)

您可以通过使Stack对象不可变来解决此问题:

object ImmutableStack {
  trait ImmutableStack[+A] {
    def isEmpty: Boolean
    def push[B >: A](item: B): ImmutableStack[B] = new <<(this, item)
    def pop: (ImmutableStack[A], Option[A])
    def <<[B >: A](item: B) = push(item)
  }
  case class <<[+A](init: ImmutableStack[A], last: A) extends ImmutableStack[A] {
    override def isEmpty = false
    override def pop: (ImmutableStack[A], Option[A]) = (init, Some(last))
    override def toString = "" + init + " << " + last
  }
  case object Nst extends ImmutableStack[Nothing] {
    override def isEmpty = true
    override def pop = (Nst, None)
    override def toString = "Nst"
  }
}

然后我们有:

scala> import ImmutableStack._
import ImmutableStack._

scala> val intStack = Nst << 5 << 4 << 3 << 2 << 1
intStack: ImmutableStack.ImmutableStack[Int] = Nst << 5 << 4 << 3 << 2 << 1

scala> intStack.pop
res0: (ImmutableStack.ImmutableStack[Int], Option[Int]) = (Nst << 5 << 4 << 3 << 2,Some(1))

scala> val firstItems << lastItem = intStack
firstItems: ImmutableStack.ImmutableStack[Int] = Nst << 5 << 4 << 3 << 2
lastItem: Int = 1

scala> val strStack = Nst << "A" << "B" << "C"
strStack: ImmutableStack.ImmutableStack[String] = Nst << A << B << C

scala> val fs << lt = strStack
fs: ImmutableStack.ImmutableStack[String] = Nst << A << B
lt: String = C

scala> val anyStack = Nst << 1 << "Str" << 'Z'
anyStack: ImmutableStack.ImmutableStack[Any] = Nst << 1 << Str << Z


scala> val st << x1 << x2 << x3 = anyStack
st: ImmutableStack.ImmutableStack[Any] = Nst
x1: Any = 1
x2: Any = Str
x3: Any = Z

答案 2 :(得分:2)

Scala的类型推理引擎因在像你这样的情况下推断Nothing而臭名昭着。简而言之,Scala的类型层次结构包含顶部(Any)和底部(Nothing),其他所有类型之间必须存在。所有类型都有Any的超类型,Nothing是所有类型的常见子类型。

这对你意味着什么?好吧,当您实例化LinkedStackGeneric实例时,省略了类型参数T的任何类型。这向Scala表明它需要推断类型(因为,作为静态类型语言,它必须在编译时确定所有类型的类型)。在这种情况下,Scala的类型推理引擎选择不佳(从用户期望的角度来看),并选择底部类型(Nothing)作为参数T的类型。有了这个,你去添加你的值“test”,Scala编译器(正确地)抱怨String无法添加到LinkedStackGeneric[Nothing],即使所有Nothing都是是Strings,并非所有(或可能不是任何)String都是Nothing

好奇的一些笔记。 Nothing实际上是所谓的无人居住类型,这意味着永远不会存在Nothing类型的实例。因此,即使我说并非所有String都是Nothing s,实际上String也不可能是Nothing