2013-04-07 15 views
6

Jeśli mam tej klasy wartość:W takich przypadkach klasa wartości Scala będzie "zapakowana", prawda?

class ActionId(val value: Int) extends AnyVal 

Następnie, we wszystkich przykładach poniżej, obiekt zostanie przydzielony do klasy wartość? (To będzie być „zapakowane” - to będzie nie prostu odwinąć do zwykłego 32-bitowej liczby całkowitej, prawda?)

  1. Funkcja, która zwraca klasę jakości - klasa wartość ucieka zakresu i więc być "zapakowane"?

    def someFunction(): ActionId = { 
        ... 
        return ActionId(123) 
    } 
    
  2. funkcję zwracającą obiektu z klasy członka wartości - klasa wartość ucieka zakresu i stąd być „zapakowane”?

    case class Post(id: ActionId, ...) { ... } 
    
    def someFunction(): Post = { 
        ... 
        val somePost = Post(ActionId(123), ...) // ActionId will be "boxed", right? 
        return somePost 
    } 
    
  3. Nawet jeśli obiekt z członkiem klasy wartość jest nie wrócił (tak naprawdę nie uciec zakres), klasa wartość będzie nadal „zapakowane”, gdy stosuje się go jako członek innej klasy (jako pole w klasie Post, w tym przykładzie)?

    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)? 
    } 
    

Wiąże się z tym this answer, który mówi, że gdy klasa wartość nie uciec zakres, to faktycznie są wstawiane. Odwołuje się do dokumentu procesu poprawy Scala SIP-15 po więcej szczegółów. O ile jednak mogę powiedzieć, że SIP-15 w rzeczywistości nie wspomina o tym, że instancja klasy wartości, która nie mieści się w zakresie, będzie "pudełkowa". Sądzę jednak, że wydaje się rozsądne, że musiałoby to być "pudełko". (Dlaczego SIP nie stwierdza wyraźnie, że będzie to pudełko, jeśli ucieknie?)

Odpowiedz

10

Żadna z Twoich przykładów spowodować boksu. Klasy wartości są tylko w pudełku z rodzajami generycznymi, z tablicami, i gdy są wpisane jako nadklasa/cecha (np. Any/AnyVal)

Są w pudełkach z rodzajami, ponieważ w przeciwnym razie nie można ich odróżnić od wartości (a prymitywy potrzebują w każdym razie pudełko). Taki sam układ z Dowolnym, a inne nadklasy/cechy wymagają pola lub relacja typu jest błędna.

Są one w pudełkach z tablicami, ponieważ tablice muszą znać typ zawartości, ale JVM nie rozumie pojęcia "typu wartości". Więc skończyłbyś z tablicą, która mówi, że był to typ boksowany, ale który Scala udawał była tablicą typu wartości; Podjęto decyzję (na podstawie wcześniejszych problemów z Array, gdy nie była to zwykła tablica Java/JVM), która prowadziłaby do zbyt wielu subtelnych błędów i narożnych przypadków.

Oto przykład z trzech sposobów, aby dostać boks:

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 

Wszystko inne - przechodząc wokół poprzez różne funkcje, itd .-- nie wymaga boks, w tym wszystkich przykładów.

Tablice są trochę przypadek szczególny, ponieważ można przechowywać prymitywów i wyciągnąć je ponownie jako klasy wartości z zerowym kodu bajtowego narzutu, ale dużo syntaktycznej napowietrznych:

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

Czy możesz wyobrazić sobie obsługę bibliotek, aby zmniejszyć obciążenie składniowe podczas pracy z tablicami? – ziggystar

+0

@ziggystar - Cóż, teraz wszystko, co zmniejsza obciążenie składniowe, zwiększa obciążenie związane z bajtem. Jest to więc trudna gra. Gdy jest to ważne, możesz mieć klasę wartości, która owija tablicę i wykonuje ją za ciebie (z bardzo minimalnym obciążeniem składniowym), ale nie jestem pewna sposobu na zrobienie trywialnej fabryki, która przy danej klasie wartości tworzy odpowiednią tablicę klasa wartości. Myślę, że makra ostatecznie się tam dostaną. –

+0

@RexKerr, więc czy to oznacza, że ​​nie ma żadnych klas wartości w strukturach danych? Innymi słowy, Lista, Mapa itp. Zawsze będą tworzyć instancję klasy pudełkowej? –

12

We wszystkich trzech przypadkach w ogóle nie będzie boksu.

Jest to dość łatwe do sprawdzenia przez siebie:

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

Teraz pozwala uruchomić scalac, a my będziemy mieć kilka plików klasowych (pliki z kodu bajtowego). Potrzebujemy 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(); 
} 

Jak widać, nawet jeśli klasa wartości przecieka z funkcji zasadniczo nie byłaby w pudełku.

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); 
} 

Jak widać ID tutaj przedstawiony jako zwykły int (np trzeciej metody).

Wreszcie, czy klasa będzie miała wartość w polu, jeśli obiekt z elementem klasy wartości nie zostanie zwrócony (tak naprawdę nie wymyka się zakresowi)?

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

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

Jeśli przyjrzymy się bliżej kodu bajtowego metody, tutaj jest to, co widzimy:

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; 

Nie ma boks z int w IdAkcji. Jeśli to pole będzie można zobaczyć coś jak ten:

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; 

Widzisz, różnica jest bipush 123 kontra

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

Czy możesz podać przykład, kiedy klasa wartość będzie pudełku? – ziggystar

+0

@ziggystar zostanie zapakowany, jeśli przeniesiemy klasę wartości do kolekcji, np. Lista –

2

Z niektórymi niejawna rzuca go jest faktycznie możliwe obejście problemu z tablicą bez składni wymaganych przez rex-kerr. Używałem go w połączeniu z 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 
} 

głównego pliku:

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) 
Powiązane problemy