问题陈述
考虑包含抽象类型成员T
的类型A
:
trait T {
type A
}
我想创建一个以T0 <: T
作为类型参数的类,但专注于类型投影 T0#A
。例如,在下文中,方法foo
可以专门化吗?
class Foo[T0 <: T] {
def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}
请注意,使用T0
注释@specialized
将无法获得所需的结果。是否有一种在类型投影foo
上专门化T#A
的好方法?
有限的解决方案:从具有额外参数的专业父类继承
在这种特殊情况下,这是一种专注于T0#A
的方法:
trait SpecializedFoo[@specialized A0, T0 <: T] {
def foo(a: A0, f: A0 => A0) = f(a)
}
class Foo2[T0 <: T] extends SpecializedFoo[T0#A, T0]
通过继承专门的父类SpecializedFoo
,我们确保Foo2.foo
是专门的。
验证专业化
要验证Foo2.foo
,而非Foo.foo
是否专业,我们可以使用明确的T
来调用它们,其中T#A
是原始的Double,
trait ExplicitT extends T {
type A = Double
}
object Test {
def test1 = (new Foo[ExplicitT]).foo(1.0, _ + 1.0)
def test2 = (new Foo2[ExplicitT]).foo(1.0, _ + 1.0)
}
可以使用命令“:javap -v Test”从REPL检查字节码,
public double test1();
Code:
Stack=4, Locals=1, Args_size=1
0: new #16; //class Foo
3: dup
4: invokespecial #18; //Method Foo."<init>":()V
7: dconst_1
8: invokestatic #24; //Method scala/runtime/BoxesRunTime.boxToDouble:(D)Ljava/lang/Double;
11: new #26; //class Test$$anonfun$test1$1
14: dup
15: invokespecial #27; //Method Test$$anonfun$test1$1."<init>":()V
18: invokevirtual #31; //Method Foo.foo:(Ljava/lang/Object;Lscala/Function1;)Ljava/lang/Object;
21: invokestatic #35; //Method scala/runtime/BoxesRunTime.unboxToDouble:(Ljava/lang/Object;)D
24: dreturn
LineNumberTable:
line 13: 0
public double test2();
Code:
Stack=5, Locals=1, Args_size=1
0: new #38; //class Foo2
3: dup
4: invokespecial #39; //Method Foo2."<init>":()V
7: dconst_1
8: new #41; //class Test$$anonfun$test2$1
11: dup
12: invokespecial #42; //Method Test$$anonfun$test2$1."<init>":()V
15: invokeinterface #48, 4; //InterfaceMethod SpecializedFoo.foo$mcD$sp:(DLscala/Function1;)D
20: dreturn
LineNumberTable:
line 14: 0
请注意,装箱会显示在test1
但不会显示在test2
。
限制
编辑7/9 上面的诀窍比我最初意识到的更有限。完全不适用于这种情况:
trait T {
type A
def x: A
def f: A => Double
}
class Foo[T0 <: T] {
def foo(t: T0) = t.f(t.x)
}
我认为没有理由(假设的)编译器原则上不能专注于A
;通常,专用版本仅在编译时已知特定T#A
时才可用。自然的实际解决方案是将A
提升为T
的类型参数,但我想知道是否可以避免这种情况。
答案 0 :(得分:1)
我看不出那是怎么回事。在编译类时完成了专业化,并且在那时,A
未知。
答案 1 :(得分:1)
这是编译器限制;一个不能通常专注于类型参数的元素。但是,建议的技巧足以满足我的目的:
trait Types {
type A
type B
}
trait GenOps[@specialized A, @specialized B] {
...
}
trait Ops[T <: Types] extends GenOps[T#A, T#B]
这种特性Ops
变得专业化,因为它继承了特征GenOps
中的专门实现。我的动机是我希望特质Ops
采用单一类型参数T
,而不是T#A
和T#B
(当Ops
也需要时,这也是必要的需要T
作为参数的较高的kinded类型。