Mic*_*tte 4 java generics scala parameterized-types
我有一些通用/ 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
}
}
Run Code Online (Sandbox Code Playgroud)
任何人都可以解释为什么当显式地将T声明为扩展AnyRef的类型的对象时,我不能返回null?
另外,如果你比我更了解Scala的泛型,请进一步解释 - 它们对我来说毫无意义(相对于C++泛型).
问题是T <: AnyRef并不意味着这一点T >: Null.
我相信唯一的反例就是Nothing.
在你IterableArrayList的类型参数上添加一个额外的(较低)类型绑定会使编译器满意:
class IterableArrayList[T >: Null <: AnyRef]() extends IterableContainer[T] {
Run Code Online (Sandbox Code Playgroud)
现在编译器知道这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
}
Run Code Online (Sandbox Code Playgroud)
使用例外的示例:
override def getAt(index: Int): T = underlyingContainer(index)
Run Code Online (Sandbox Code Playgroud)
请注意,如果索引无效,则底层ArrayBuffer已经抛出IndexOutOfBoundsException.如果用户传入负索引,这还具有正常工作的附加优势...(0 <= index如果您继续使用,可能需要添加一个条件Option.)
| 归档时间: |
|
| 查看次数: |
1052 次 |
| 最近记录: |