繁体   English   中英

Scala泛型混乱

[英]Scala generics Confusion

我有一些通用/ OOP Scala代码(没用,只是课堂练习)

我有一个Container接口,“IterableContainer”,它接收并返回其类型是AnyRef的子类的对象。 它有一个具体的子类,它也接受并返回其类型是AnyRef的子类的对象。

  trait IterableContainer[Type <: AnyRef] {

    def length: Int

    def getAt(index: Int): Type

    def append(element: Type)
  }

  class IterableArrayList[T <: AnyRef]() extends IterableContainer[T] {

    val underlyingContainer = new ArrayBuffer[T](16)

    override def length: Int = {
      return underlyingContainer.length
    }

    override def getAt(index: Int): T =
    {
      if (index < underlyingContainer.length) {
        return underlyingContainer(index)
      } else {
        return null // Expression of type T does not conform to expected type. 
      }
    }

    override def append(element: T) = {
      underlyingContainer :+ element // :+ means append
    }
  }

任何人都可以解释为什么当显式地将T声明为扩展AnyRef的类型的对象时,我不能返回null?

另外,如果你比我更了解Scala的泛型,请进一步解释 - 它们对我来说毫无意义(相对于C ++泛型)。

问题是T <: AnyRef并不意味着T >: Null

我相信唯一的反例是Nothing

IterableArrayList的类型参数上添加额外的(较低)类型绑定会使编译器满意:

class IterableArrayList[T >: Null <: AnyRef]() extends IterableContainer[T] {

现在,编译器知道T是一个亚型AnyRef 的超Null


现在,您可能会想到自己, “这不会导致在Scala中编写库代码的各种问题吗?人们必须要添加T >: Null无处不在!”

然而,答案是否定的。

为什么? 因为Scala程序员几乎从不使用null

我们以List为例。 获取列表的第一个元素有两种方法:

def head: A (scaladoc)

def headOption: Option[A] (scaladoc)

如果您在空列表上调用head ,则会出现异常。 如果在空列表上调用headOption ,则会得到None

这是Scala库的常用策略:要么抛出异常,要么返回Option类型 - 永远不会返回null

使用Option[T]示例:

override def getAt(index: Int): Option[T] =
  if (index < underlyingContainer.length) {
    Some(underlyingContainer(index))
  } else {
    None
  }

使用例外的示例:

override def getAt(index: Int): T = underlyingContainer(index)

请注意,如果索引无效,则基础ArrayBuffer已抛出IndexOutOfBoundsException 如果用户传入负索引,这还具有正常工作的附加优势...(如果使用Option可能需要为条件添加0 <= index 。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM