Duplicate methods due to type erasure despite @specialized

Stumbled upon this

def foo(f: Int => Unit) {} def foo(f: Long => Unit) {} 

not compiled due to method foo is defined twice . I know that the above is only a shorthand for

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

and that after deleting the type, both methods have the same signature.

Now I read in Try the specialized Function1 / Function2 in 2.8.0 RC1! that Function1 and Function2 have @specialized versions for Int , Long and Double , since Scala 2.8. This, of course, means that Function[Int, Unit] and Function[Long, Unit] have separate class files at the JVM level.

Isn't there two different signatures?

Is there a problem that the parameter of the second type will still be erased? But same problem with

 class Bar[@specialized T] def foo(f: Bar[Int]) {} def foo(f: Bar[Long]) {} 

It does not compile.

+6
source share
3 answers

@specialized has nothing to do with type erasure, at least in this case. This means that an additional version of your class is generated with a native type in position. This is especially noticeable when boxing / unpacking.

So you define a class like:

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

and you will get two classes as output, (approximately):

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

You need to have two class implementations, because you cannot always guarantee that someone who has the correct native type will be called. The scala compiler will choose which one to invoke. Please note that the java compiler has no idea that this specialization takes place, so you need to call non-specialized methods.

In fact, the output is as follows (via 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() {} } 

Thus, the problem of erasing your type will not be fixed with @specialized.

+6
source

Both for compatibility and for cases when parameters of type Function1 unknown, a method with a signature must also be generated, as if Function1 not specialized.

+3
source

Inspired by Matthew Farwell in particular Answer: I tried the following

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

with scalac -print and got:

 // 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(bt()); 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) 

But only the non-specialized version of val t accessor is used through foo , even for the specialized instance of bari and the indirect bari override method def t(): Object = Int.box(Bar$mcI$sp.this.t()); .

0
source

Source: https://habr.com/ru/post/901493/


All Articles