尽管有@specialized,但由于类型擦除导致重复的方法

时间:2011-11-15 09:18:39

标签: scala type-erasure

偶然发现了

def foo(f: Int => Unit) {}
def foo(f: Long => Unit) {}
由于method foo is defined twice

无法编译。我知道上面只是

的简写
def foo(f: Function1[Int, Unit]) {}
def foo(f: Function1[Long, Unit]) {}

并且在类型擦除之后,两种方法都具有相同的签名。

现在我在Try out specialized Function1/Function2 in 2.8.0 RC1!中读到Function1Function2 @specialized IntLongDouble版本自Scala 2.8起。这肯定意味着Function[Int, Unit]Function[Long, Unit]在JVM级别具有单独的类文件。

那么两个签名都不会有所不同吗?

问题是,第二个类型参数将继续被删除吗?但与

相同的问题
class Bar[@specialized T]
def foo(f: Bar[Int]) {}
def foo(f: Bar[Long]) {}

它无法编译。

3 个答案:

答案 0 :(得分:6)

@specialized与类型擦除无关,至少在这种情况下。这意味着将使用位置中的本机类型生成类的额外版本。这显着节省了装箱/拆箱。

所以你定义了一个类:

class MyClass[@specialized(Int) T] {
  def foobar(t: T) = {}
}

你得到两个类作为输出,(大约):

class Foobar[java.lang.Object] {
  def foobar(t: java.lang.Object) = {}
}

class Foobar[int] {
  def foobar(t: int) = {}
}

您需要拥有该类的两个实现,因为您无法始终保证将调用具有正确本机类型的实现。 scala编译器将选择要调用的编译器。请注意,java编译器不知道正在进行此专业化,因此必须调用非专用方法。

实际上,输出如下(通过JAD):

public class MyClass implements ScalaObject {
    public void foobar(Object obj) { }

    public void foobar$mcI$sp(int t) {
        foobar(BoxesRunTime.boxToInteger(t));
    }

    public MyClass() { }
}

public class MyClass$mcI$sp extends MyClass {
    public void foobar(int t) {
        foobar$mcI$sp(t);
    }

    public void foobar$mcI$sp(int i) { }

    public volatile void foobar(Object t) {
      foobar(BoxesRunTime.unboxToInt(t));
    }

    public MyClass$mcI$sp() {}
}

所以你的类型擦除问题不会被@specialized修复。

答案 1 :(得分:3)

对于兼容性和Function1的类型参数未知的情况,还必须生成具有签名的方法,如同Function1不是专用的。

答案 2 :(得分:0)

尤其受到Matthew Farwell答案的启发我尝试了以下

class Bar[@specialized(Int) T](val t: T)
class Foo {
  def foo(b: Bar[_]) { print(b.t) }
}
val bari = new Bar(1)
print(bari.t)
foo(bari)

scalac -print并获得:

// unspecialized version Bar[_] = Bar[Object]
class Bar extends Object with ScalaObject {
  protected[this] val t: Object = _;
  def t(): Object = Bar.this.t;
  def t$mcI$sp(): Int = Int.unbox(Bar.this.t());
  def specInstance$(): Boolean = false;
  def this(t: Object): Bar = {
    Bar.this.t = t;
    Bar.super.this();
    ()
  }
};
// specialized version Bar[Int]
class Bar$mcI$sp extends Bar {
  protected[this] val t$mcI$sp: Int = _;
  // inside of a specialized class methods are specialized,
  // so the `val t` accessor is compiled twice:
  def t$mcI$sp(): Int = Bar$mcI$sp.this.t$mcI$sp;
  override def t(): Int = Bar$mcI$sp.this.t$mcI$sp();
  def specInstance$(): Boolean = true;
  override def t(): Object = Int.box(Bar$mcI$sp.this.t());
  def this(t$mcI$sp: Int): Bar$mcI$sp = {
    Bar$mcI$sp.this.t$mcI$sp = t$mcI$sp;
    Bar$mcI$sp.super.this(null);
    ()
  }
}
class Foo extends Object with ScalaObject {
  // scalac compiles only ONE foo method not one for every special case
  def foo(b: Bar): Unit = Predef.print(b.t());
  def this(): Foo = {
    Foo.super.this();
    ()
  }
};
val bari: or.gate.Bar = new or.gate.Bar$mcI$sp(1);
// specialized version of `val t` accessor is used:
Predef.print(scala.Int.box(bari.t$mcI$sp()));
Foo.this.foo(bari)

但是通过foo仅使用非专业版val t访问者,即使对于专门的实例bari和间接bari的重写方法{{ 1}}被称为。