2013-07-15 37 views
20

斯卡拉2.10介绍value classes。它们对编写类型安全代码非常有用。此外还有一些限制,其中一些会被编译器检测到,另一些则需要在运行时进行分配。斯卡拉。一个字段的案例类可以是一个值类吗?

我想使用case class语法创建值类,以允许创建没有新语法和人性化的toString。没有模式匹配,导致它需要分配。

所以问题是:会用case class语法要求值类的分配吗?

回答

23

您可以拥有一个值类的案例类。从下面的例子可以看出,没有创建对象。当然,如果你会向Any发起冲击,那么必然会出现拳击。

这里是一小块Scala代码

class ValueClass(val value:Int) extends AnyVal 

case class ValueCaseClass(value:Int) extends AnyVal 

class ValueClassTest { 

    var x: ValueClass = new ValueClass(1) 

    var y: ValueCaseClass = ValueCaseClass(2) 

    def m1(x:ValueClass) = x.value 

    def m2(x:ValueCaseClass) = x.value 
} 

而且字节码,不包含两个值类的丝毫痕迹。

Compiled from "ValueClassTest.scala" 
public class ValueClassTest { 
    public int x(); 
    Code: 
     0: aload_0  
     1: getfield  #14     // Field x:I 
     4: ireturn  

    public void x_$eq(int); 
    Code: 
     0: aload_0  
     1: iload_1  
     2: putfield  #14     // Field x:I 
     5: return   

    public int y(); 
    Code: 
     0: aload_0  
     1: getfield  #21     // Field y:I 
     4: ireturn  

    public void y_$eq(int); 
    Code: 
     0: aload_0  
     1: iload_1  
     2: putfield  #21     // Field y:I 
     5: return   

    public int m1(int); 
    Code: 
     0: iload_1  
     1: ireturn  

    public int m2(int); 
    Code: 
     0: iload_1  
     1: ireturn  

    public rklaehn.ValueClassTest(); 
    Code: 
     0: aload_0  
     1: invokespecial #29     // Method java/lang/Object."<init>":()V 
     4: aload_0  
     5: iconst_1  
     6: putfield  #14     // Field x:I 
     9: aload_0  
     10: iconst_2  
     11: putfield  #21     // Field y:I 
     14: return   
} 
2

至少在“允许创建,而无需新的语法”,您可以使用普通的旧方法或object s的apply方法。 toString也不是问题(如果我没有记错的话),但是如果你没有使用case class,你必须自己定义它。
顺便说一句,语言允许限定延伸AnyVal情况下类,请参见http://docs.scala-lang.org/overviews/core/value-classes.html

1

参见overview documentation部分“当分配是必要的”。

案例类收到特别通知:“注意:您可以在实践中使用案例类和/或扩展方法来获得更清晰的语法。”

但是,正如@吕迪格 - klaehn已经表示,需要注意的例子,其中提供的任何预期的AnyVal:

package anything 

// the caveat from the overview 
trait Distance extends Any 
case class Meter(val value: Double) extends AnyVal with Distance 

class Foo { 
    def add(a: Distance, b: Distance): Distance = Meter(2.0) 
} 

object Test extends App { 
    val foo = new Foo 
    Console println foo.add(Meter(3.4), Meter(4.3)) 
} 

显示的是:javap的-app固定在最新的2.11:

scala> :javap -app anything.Test$ 
    public final void delayedEndpoint$anything$Test$1(); 
    flags: ACC_PUBLIC, ACC_FINAL 
    Code: 
     stack=7, locals=1, args_size=1 
     0: aload_0  
     1: new   #63     // class anything/Foo 
     4: dup   
     5: invokespecial #64     // Method anything/Foo."<init>":()V 
     8: putfield  #60     // Field foo:Lanything/Foo; 
     11: getstatic  #69     // Field scala/Console$.MODULE$:Lscala/Console$; 
     14: aload_0  
     15: invokevirtual #71     // Method foo:()Lanything/Foo; 
     18: new   #73     // class anything/Meter 
     21: dup   
     22: ldc2_w  #74     // double 3.4d 
     25: invokespecial #78     // Method anything/Meter."<init>":(D)V 
     28: new   #73     // class anything/Meter 
     31: dup   
     32: ldc2_w  #79     // double 4.3d 
     35: invokespecial #78     // Method anything/Meter."<init>":(D)V 
     38: invokevirtual #84     // Method anything/Foo.add:(Lanything/Distance;Lanything/Distance;)Lanything/Distance; 
     41: invokevirtual #88     // Method scala/Console$.println:(Ljava/lang/Object;)V 
     44: return   
     LocalVariableTable: 
     Start Length Slot Name Signature 
       0  45  0 this Lanything/Test$; 
     LineNumberTable: 
     line 11: 0 
     line 12: 11 
} 

有我们被警告的拳击。

(更新:。其实,要解决-app公关尚未合并,敬请关注)

5

要扩展这个问题,Wojciech Langiewicz提出作为案例类的Value class一个很好的例子。

代替:

case class Player(id: Int, gameCash: Int, gameCoins: Int, energy: Int) 

沃伊切赫定义:

case class Player(id: PlayerId, gameCash: GameCash, gameCoins: GameCoins, energy: Energy) 

与壳体类(没有附加的对象的分配在堆上):

case class PlayerId(id: Int) extends AnyVal 
case class GameCash(value: Int) extends AnyVal 
case class GameCoins(value: Int) extends AnyVal 
case class Energy(value: Int) extends AnyVal 

当创建case类只包含一个参数y您应该添加extends AnyVal以允许Scala编译器运行更多优化 - 基本上类型检查将仅在编译阶段完成,但在运行期间只有只会创建基础类型的对象,从而导致内存开销更少

在我们的代码中的特定点添加自定义类型不仅提高了可读性,还允许我们减少错误 - 将一些否则必须在测试中完成的检查(或根本没有完成)卸载到编译器。您也可以立即在IDE或编辑器中看到错误。

因为现在Player类中的每个组件本身都是一个单独的类型,所以添加新操作符也很容易,否则可能必须隐式添加新的操作符,这会污染更大的范围。