扩展AnyVal和AnyRef

时间:2016-04-13 05:56:44

标签: java scala memory-management

因此在Java中,我们看到原始类型占用的内存空间少于它们的对象。例如,int占用的空间比Integer的实例少。 出于这个原因,char代替String来存储单个字符通常更为明智。

我想了解这是如何转化为Scala的。 没有基元。我们所拥有的只是AnyValAnyRef的实例。 每个内存分配之间是否存在差异?

3 个答案:

答案 0 :(得分:3)

当然有些原始人。斯卡拉只是(故意)试图隐藏差异。

实际上有4个案例,而不是2个案例:

  1. 扩展AnyRef的类。它们只是通常的Java风格的类,并且具有相同的内存使用。

  2. Unit。它对应于Java void,因此通常根本不在运行时表示。如果是,则使用BoxedUnit类。

  3. IntBoolean等。虽然它们看起来像Scala代码的类,但它们通常表示为JVM原语。但是在某些情况下它们不能用,而是使用盒装类型(Integer等)。也就是说,当它们是Array的的泛型类型/方法的参数时。

  4. 用户定义的值类。请参阅Yuval Itzchakov的答案。当然,如果你有一个包含一个原语的值类,它最终会被表示为一个原语本身。请注意,它们不是始终在运行时作为基础类型表示,特别是Array[ValueClass]失败,与案例3相反。

答案 1 :(得分:2)

  

每个

的内存分配是否有差异

AnyRef类似于inherting object,由于标题,每个对象分别为x86 / x64获得8/16字节的正常开销。

Scala中的

AnyVal允许优化一个非常具体的用例,其中有一个val参数,该参数包含在class中,您可以将所述类表示为底层运行时的值,而不是分配类的实例。

AnyVal有几个限制:

  
      
  1. 必须只有一个主构造函数,其中只有一个public,val参数,其类型不是值类。 (来自Scala 2.11.0 ,.   参数可能是非公开的。)
  2.   
  3. 可能没有专门的类型参数。
  4.   
  5. 可能没有嵌套或本地类,特征或对象
  6.   
  7. 可能无法定义equals或hashCode方法。
  8.   
  9. 必须是顶级类或静态可访问对象的成员
  10.   
  11. 只能将defs作为成员。特别是,它不能将lazy vals,vars或val作为成员。
  12.   
  13. 不能由其他班级延长。
  14.   

例如,如果我们采用示例from the documentation

class Wrapper(val underlying: Int) extends AnyVal {
  def foo: Wrapper = new Wrapper(underlying * 19)
}

编译时Wrapper的表示仍然是一个类。但在运行时,基础表示将是Int,而不是具有额外Wrapper字段的类Int

但并不总能保证Wrapper在运行时会成为Int表示。它不会在以下时间:

  1. 值类被视为另一种类型。
  2. 将值类分配给数组。
  3. 进行运行时类型测试,例如模式匹配。

答案 2 :(得分:1)

Scala 中, Scala 在编译时使用框/ unbox BoxesRunTime.java来处理基本类型:

  • 框表示将基元类型解析为引用类型

字节代码

def foo[T](v: T) = v.toString
def bar(i: Int)= foo(i)
:javap -c bar
public java.lang.String bar(int);
  Code:
     0: getstatic     #19                 // Field .MODULE$:L;
     3: iload_1
     4: invokestatic  #25                 // Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
     7: invokevirtual #29                 // Method .foo:(Ljava/lang/Object;)Ljava/lang/String;
    10: areturn

bar方法的上述字节代码中,它将原始int类型加到 Int引用类型。这是因为我们使用原始类型int来表示泛型T

  • unbox表示将引用类型解析为原始类型

字节代码:

 def foo(i: Int) = i
    def bar[T](i: T) = foo(i.asInstanceOf[Int])
    public <T> int bar(T);
     Code:
        0: getstatic     #19                 // Field .MODULE$:L;
        3: aload_1
        4: invokestatic  #24                 // Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
        7: invokevirtual #28                 // Method .foo:(I)I

在bar方法的上述字节代码中,我们将引用类型解析为原始Int类型,因此需要将其解包为基本类型int < /强>

所以在运行时,总是有用于类型解析的box或unbox。它可能会导致性能不佳并消耗更多内存。但我们可以通过以下方式避免它:

它们都用于解决 scala 中的 box / unbox 问题。