2

我正在尝试实现一个不会为包装器消耗额外内存的选项。我创建一个类。Null 代表 None,非 null 值代表 Some。

class Maybe[+T](private val nullable: T) extends AnyVal {
  @inline final def isDefined: Boolean = {
    println("isDefined called")
    val res = nullable != null
    println(s"result = $res")
    res
  }
  @inline final def get: T = if (isDefined) nullable else throw new NoSuchElementException
  @inline final def getOrElse[B >: T](default: => B): B = if (isDefined) this.get else default
  @inline final def map[B](f: T => B) : Maybe[B] = if (isDefined) Maybe.some(f(this.get)) else Maybe.none
  @inline final def flatMap[B](f: T => Maybe[B]): Maybe[B] = if (!isDefined) Maybe.none else f(get)
  def toOption : Option[T] = if (isDefined) Some(get) else None
}

object Maybe {
  def some[T](value:T) : Maybe[T] = new Maybe(value)

  final val none : Maybe[Nothing] = {
    println("start initializing none")
    val v = new Maybe(null)
    println("1")
    val res = v.asInstanceOf[Maybe[Nothing]]
    println("2")
    res
  }
}

object MaybeTest extends App {
  println("try to create Maybe.none")
  val myNone = Maybe.none
  println("Initialized")
  println(myNone.isDefined)
  println("Accessed")
}

但是当我尝试运行它时,我得到了一个 NPE:

尝试创建 Maybe.none

开始初始化无

1

2

初始化

在 com.MaybeTest$.delayedEndpoint$com$MaybeTest$1(Maybe.scala:34) 在 com.MaybeTest$delayedInit$body.apply(Maybe.scala:30) 在 scala 的线程“main”java.lang.NullPointerException 中的异常。 Function0$class.apply$mcV$sp(Function0.scala:34) at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) at scala.App$$anonfun$main$1.apply(App. scala:76) at scala.App$$anonfun$main$1.apply(App.scala:76) at scala.collection.immutable.List.foreach(List.scala:392) at scala.collection.generic.TraversableForwarder$class .foreach(TraversableForwarder.scala:35) at scala.App$class.main(App.scala:76) at com.MaybeTest$.main(Maybe.scala:30) at com.MaybeTest.main(Maybe.scala)

如果我删除“扩展 AnyVal”,一切正常。谁能解释这种行为?

4

1 回答 1

3

@Jasper-M 的评论解释了为什么您的解决方案无法工作,但这很接近:

class Maybe[+T] private (private val nullable: Object) extends AnyVal {
  @inline private final def get0 = nullable.asInstanceOf[T]
  @inline final def isDefined: Boolean = nullable != null
  @inline final def get: T = if (isDefined) get0 else throw new NoSuchElementException
  @inline final def getOrElse[B >: T](default: => B): B = if (isDefined) get0 else default
  @inline final def map[B](f: T => B) : Maybe[B] = if (isDefined) Maybe.some(f(get0)) else Maybe.none
  @inline final def flatMap[B](f: T => Maybe[B]): Maybe[B] = if (!isDefined) Maybe.none else f(get0)
  @inline final def toOption: Option[T] = if (isDefined) Some(get0) else None
}

object Maybe {
  def some[A](value: A) : Maybe[A] = {
    val value1 = value.asInstanceOf[Object]
    assert(value1 != null)
    new Maybe[A](value1)
  }

  private val _none = new Maybe[Null](null)
  def none[A]: Maybe[A] = _none.asInstanceOf[Maybe[A]]

  def apply[A](value: A) = new Maybe[A](value.asInstanceOf[Object])
}

但它并不是真正的“Option包装器不会消耗额外内存”:它只是null用另一个名字来调用。例如Option[Option[A]],按预期工作,但Maybe[Maybe[A]]没有(编译器不会告诉你!)。

于 2017-04-27T12:55:56.030 回答