Дублирующиеся методы из-за стирания типа, несмотря на @specialized - PullRequest
6 голосов
/ 15 ноября 2011

Наткнулся на это

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]) {}

и что после стирания типа оба метода имеют одинаковую сигнатуру.

Теперь я прочитал в Попробуйте специализированные функции Function1 / Function2в 2.8.0 RC1! , что Function1 и Function2 имеют @specialized версии для Int, Long и Double начиная с 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 ]

6 голосов
/ 15 ноября 2011

@ special не имеет отношения к стиранию типов, по крайней мере, в этом случае. Это означает, что дополнительная версия вашего класса генерируется с собственным типом в позиции. Это существенно экономит на упаковке / распаковке.

Итак, вы определяете класс как:

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() {}
}

Так что ваша проблема стирания типа не будет решена с помощью @ special.

3 голосов
/ 15 ноября 2011

Как для совместимости, так и для случаев, когда параметры типа Function1 неизвестны, также должен быть создан метод с сигнатурой, как если бы Function1 не был специализированным.

0 голосов
/ 15 ноября 2011

Вдохновлен особенно 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 def t(): Object = Int.box(Bar$mcI$sp.this.t());.

...