2013-04-07 32 views
6

如果我有這樣的價值類:在這些情況下,Scala值類將被「裝箱」,對吧?

class ActionId(val value: Int) extends AnyVal 

然後,在下面的所有例子,一個對象將被分配給值類? (它會「盒裝」 - 它會不會簡單被解開,以一個普通的32位整數,對吧?)

  1. 返回一個值類的函數 - 值類逃脫範圍,將因此是「盒裝」?

    def someFunction(): ActionId = { 
        ... 
        return ActionId(123) 
    } 
    
  2. 與值類構件返回一個對象的函數 - 的值類逸出範圍和此後將「盒裝」?

    case class Post(id: ActionId, ...) { ... } 
    
    def someFunction(): Post = { 
        ... 
        val somePost = Post(ActionId(123), ...) // ActionId will be "boxed", right? 
        return somePost 
    } 
    
  3. 即使用值類成員對象是返回(並沒有真正逃脫的範圍),該值類仍然會「盒裝」,當它被用作另一個班的成員(在本例中,作爲Post班的一個領域)?

    def anotherFunction() { 
        ... 
        val somePost = Post(ActionId(123), ...) // "Boxed" here too, right? 
    
        // ... do something with somePost 
    
        // But don't: return somePost 
    
        // However some *other* similar functions *do* return `somePost` — so 
        // class `Post` must be able to box the ActionId? Hence it's boxed (above)? 
    } 
    

與此相關的是this answer,它說,當值類沒有逃脫範圍,它有效地被內聯。有關更多詳細信息,請參閱Scala改進流程文檔SIP-15。但是據我所知,SIP-15實際上並沒有提到逃離範圍的值類實例將被「裝箱」。但我認爲它必須被「裝箱」似乎是合理的。 (爲什麼SIP沒有明確說明,如果它逃脫,它將被裝箱?)

回答

10

沒有你的例子導致拳。值類只與泛型,數組,以及輸入爲超類/特徵(例如Any/AnyVal)

它們裝有泛型,否則無法區分它們的值(和原語需要反正是一個盒子)。與Any相同,其他超類/特徵需要一個盒子或者類型關係是錯誤的。

它們裝有數組,因爲數組需要知道內容的類型,但JVM不理解「值類型」的概念。所以你最終會得到一個數組,表示它是被裝箱的類型,但是Scala假裝的是一個值類型的數組;做出了一個決定(基於之前與Array有關的問題,當它不僅僅是一個普通的Java/JVM數組時),這會導致太多微妙的錯誤和特例。

這裏有三種方式爲例來獲得拳擊:

trait Q extends Any {} 
class X(val x: String) extends AnyVal with Q {} 

// Array 
val a = Array(new X("salmon")) // boxed 

// Generic 
val b = Option(new X("minnow")) // boxed 

// Upcast 
val c = (new X("perch"): Any) // boxed 
val d = (new X("cod"): AnyVal) // boxed 
val e = (new X("herring"): Q) // boxed 

一切 - 繞過通過各種功能,等等 - 不需要拳,包括所有的例子。

數組是一個有點特殊情況的,因爲你可以存儲的圖元,並再次將它們拉出來的價值類與零字節碼的開銷,但很多語法開銷:

class Y(val repr: String) extends AnyVal {} 
val y1 = new Y("apple") // unboxed 
val y2 = new Y("orange") // unboxed 
val ys: Array[String] = Array(y1.repr, y2.repr) // no overhead 
val y3 = new Y(ys(0))  // no overhead 
+0

您可以想象庫支持在處理數組時處理語法開銷嗎? – ziggystar 2013-04-07 21:25:42

+0

@ziggystar - 那麼,現在所有減少語法開銷的東西都會增加字節碼開銷。所以這是一個難以玩的遊戲。當它很重要時,你可以有一個值類包裝數組併爲你完成工作(只需很少的語法開銷),但我不確定如何使一個簡單的工廠,給定一個值類產生相應的數組價值班。我想,宏將最終到達那裏。 – 2013-04-07 21:30:02

+0

@RexKerr這是否意味着數據結構中沒有值類?換句話說,List,Map等將始終實例化盒裝類? – 2015-05-12 21:16:30

12

在所有三種情況下,根本沒有裝箱。

這是很容易通過你的自我檢查:

class ActionId(val value: Int) extends AnyVal 
object Foo { 
    def someFunction(): ActionId = { 
    new ActionId(123) 
    } 
} 

現在讓我們來運行scalac,我們將有一大堆的類文件(文件與字節碼)的。我們需要的是Foo \ $。

» javap Foo\$ 
Compiled from "Boxing.scala" 
public final class Foo$ extends java.lang.Object{ 
    public static final Foo$ MODULE$; 
    public static {}; 
    public int someFunction(); 
} 

正如您所看到的,即使值類通常從函數泄漏,它也不會被裝箱。

case class Post(id: ActionId, notion: String) 

object Foo2 { 
    def someFunction(): Post = { 
    Post(new ActionId(123), "So ActionID will be boxed?") 
    } 
} 

scalac => javap的:

» javap Post 
Compiled from "Boxing.scala" 
public class Post extends java.lang.Object implements scala.Product,scala.Serializable{ 
    public static scala.Function1 tupled(); 
    public static scala.Function1 curried(); 
    public int id(); 
    public java.lang.String notion(); 
    public Post copy(int, java.lang.String); 
    public int copy$default$1(); 
    public java.lang.String copy$default$2(); 
    public java.lang.String productPrefix(); 
    public int productArity(); 
    public java.lang.Object productElement(int); 
    public scala.collection.Iterator productIterator(); 
    public boolean canEqual(java.lang.Object); 
    public int hashCode(); 
    public java.lang.String toString(); 
    public boolean equals(java.lang.Object); 
    public Post(int, java.lang.String); 
} 

正如你可以看到ID這裏表示爲普通int(例如第三方法)。

最後,如果帶有值類成員的對象沒有被返回(真的不會逃避作用域),那麼值類會被裝箱嗎?

case class Post(id: ActionId, notion: String) 

object Foo3 { 
    def anotherFunction(): Unit { 
    val post = Post(new ActionId(123), "Will be boxed?") 
    } 
} 

如果我們在方法的字節碼仔細一看,這裏就是我們將看到:

Code: 
    Stack=4, Locals=2, Args_size=1 
    0: new #15; //class Post 
    3: dup 
    4: bipush 123 
    6: ldC#17; //String Will be boxed? 
    8: invokespecial #20; //Method Post."<init>":(ILjava/lang/String;)V 
    11: astore_1 
    12: return 
    LocalVariableTable: 
    Start Length Slot Name Signature 
    0  13  0 this  LFoo3$; 
    12  0  1 post  LPost; 

沒有在ActionId INT的拳擊。如果將框,你會看到這樣的事情之一:

Code: 
    Stack=5, Locals=2, Args_size=1 
    0: new #15; //class Post 
    3: dup 
    4: new #17; //class ActionId 
    7: dup 
    8: bipush 123 
    10: invokespecial #20; //Method ActionId."<init>":(I)V 
    13: ldC#22; //String Will be boxed? 
    15: invokespecial #25; //Method Post."<init>":(LActionId;Ljava/lang/String;)V 
    18: astore_1 
    19: return 
    LocalVariableTable: 
    Start Length Slot Name Signature 
    0  20  0 this  LFoo3$; 
    19  0  1 post  LPost; 

你看,所不同的是bipush 123

4: new #17; //class ActionId 
    7: dup 
    8: bipush 123 
    10: invokespecial #20; //Method ActionId."<init>":(I)V 
+0

你能不能給當值類將框的例子嗎? – ziggystar 2013-04-07 20:03:49

+0

@ziggystar如果將值類放入集合中,它將被裝箱列表 – 2013-04-07 20:13:08

2

隨着一些隱式類型轉換它實際上可以在沒有rex-kerr所需的語法的情況下解決數組問題。我用它連同How to reduce the number of objects created in Scala?

Y.scala:

import scala.language.implicitConversions 

class Y(val repr: String) extends AnyVal {} 
object Y { 
    implicit def stringToY (v:String) = new Y(v) 
    implicit def yToString (v:Y) = v.repr 
} 

主文件:

import Y._ 

val y1 = new Y("apple") // unboxed 
val y2 = new Y("orange") // unboxed 
val ys: Array[String] = Array(y1, y2) // Implicit cast 
val y3:Y = ys(0)