2017-04-27 50 views
1

我試圖實現一個不佔用額外內存的選項。 我創建了一個類。空值表示無,非空值表示某些。創建Anyval選項的另一個嘗試

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

開始初始化沒有

初始化

異常在線程 「主」 顯示java.lang.NullPointerException在 com.MaybeTest $ $ .delayedEndpoint COM $ MaybeTest $ 1(Maybe.scala:34) 在 com.MaybeTest $ $ delayedInit body.apply(也許。 scala:30) at scala.Function0 $ class.apply $ mcV $ sp(Function0.scala:34)at scala.runtime.AbstractFunction0.apply $ mcV $ sp(AbstractFunction0.scala:12) at scala.App $ $ $ anonfun主$ 1.適用(App.scala:76)在 scala.App $$ anonfun $ $主1.適用(App.scala:76)在 scala.collection.immutable.List.foreach(List.scala: 392)at scala.collection.generic.TraversableForwarder $ class.foreach(TraversableForwarder.scala:35) at sc ala.App $ class.main(App.scala:76)在 com.MaybeTest $。主要(Maybe.scala:30) com.MaybeTest.main(Maybe.scala)

如果我刪除'擴展AnyVal'一切正常。 任何人都可以解釋這種行爲嗎?

+1

所以...你想介紹一個'null' ......這更是一個運行時實體的,不能在編譯時區分到您的「無空所謂的」生態系統。然後你甚至努力去做。做得好。 –

+2

Scala在運行時表示'Maybe [Int]'作爲'Int'。 Scala的類型系統要求'Nothing'被零值居住,甚至不爲零。然而它會在運行時找到一個null來表示你的'Maybe [Nothing]'。奇怪的事情會發生。 –

+0

@ Jasper-M 合理,謝謝。是否存在更好的實施? – simpadjo

回答

2

@碧玉-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]]沒有(並且編譯器不會告訴你!)。

+0

您的解決方案不適用於T <:AnyVal。 順便說一句,如果你只想支持T <:AnyRef,那麼不需要使用不安全的強制轉換:只需在原始解決方案中使用Null而不是Nothing。 – simpadjo

+0

它似乎對我很好:例如'Maybe.none [Int] .isDefined'爲false,'Maybe.some(1).get'爲'1'。什麼不符合你的期望?如果你想'Maybe [Int]'被表示爲'Int',那麼正如我在開始時所說的那樣,Jasper-M的評論解釋了爲什麼你不能做到這一點,而沒有使價值不可代表, Option'。 –

+0

(實際上,我不確定你甚至可以用「0」以外的值作爲不可代表的值)。 –

相關問題