问题陈述
考虑一个T
包含抽象类型成员的类型A
:
trait T {
type A
}
Run Code Online (Sandbox Code Playgroud)
我想创建一个类T0 <: T
作为类型参数,但专注于类型投影 T0#A
.例如,在下面,该方法foo
可以是专用的吗?
class Foo[T0 <: T] {
def foo(a: T0#A, f: T0#A => T0#A) = f(a)
}
Run Code Online (Sandbox Code Playgroud)
需要注意的是注释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]
Run Code Online (Sandbox Code Playgroud)
通过继承专门的父类SpecializedFoo
,我们确保它Foo2.foo
是专门的.
验证专业化
为了验证Foo2.foo
(但不是Foo.foo
)是专门的,我们可以使用显式T
where 来调用它们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)
}
Run Code Online (Sandbox Code Playgroud)
可以使用命令":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
Run Code Online (Sandbox Code Playgroud)
请注意拳击出现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)
}
Run Code Online (Sandbox Code Playgroud)
我认为没有理由(假设的)编译器A
原则上不能专攻; 通常,专用版本只有T#A
在编译时知道特定版本时才可用.自然的实际解决方案是提升A
到一个类型参数T
,但我想知道我是否可以避免这种情况.
这是编译器的限制;通常不能专门研究类型参数的元素。然而,所提出的技巧对于我的目的来说已经足够好了:
trait Types {
type A
type B
}
trait GenOps[@specialized A, @specialized B] {
...
}
trait Ops[T <: Types] extends GenOps[T#A, T#B]
Run Code Online (Sandbox Code Playgroud)
这样,特征Ops
就变得特殊化了,因为它继承了特征中的特殊实现GenOps
。我的动机是我希望 TraitOps
采用单个类型参数T
,而不是同时采用T#A
and (当还采用需要作为参数的更高类型类型T#B
时,这变得必要)。Ops
T
归档时间: |
|
查看次数: |
924 次 |
最近记录: |