[英]implementing a trait with a type parameter def fails to compile
我想我可能会做一些非常愚蠢的事情,但是下面的片段不能编译。
scala> case class Foo1(i: Int)
defined class Foo1
scala> trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
defined trait Foo1
然后实现如下特征
scala> implicit val foodVal = new Foo1{
| def testFoo[Foo1](l:List[Foo1] ) = {
| for{
| a <- l
| if (a.i == 1)
| } yield a
| }
| }
<console>:13: error: value i is not a member of type parameter Foo1
更新感谢您的回复。 我真的很想知道在trait
级别和def
级别之间传递类型之间的区别。 在答案中,我可以看到类型是在一个特征级别传递,它确实有效,但后面仍然给我相同的错误
scala> trait Bar { def testFoo[T](l1: List[T] ) : List[T] }
defined trait Bar
scala> implicit val fooVal = new Bar {
| override def testFoo[Foo1](l:List[Foo1]) : List[Foo1] = {
| for { a <- l if a.i == 1} yield a
| }
| }
<console>:18: error: value i is not a member of type parameter Foo1
在REPL,定义:
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
正在取代类定义
case class Foo1(i: Int)
如果您尝试使用paste命令执行相同的操作,您将看到两者无法一起定义:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo1(i: Int)
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
// Exiting paste mode, now interpreting.
<console>:36: error: Foo1 is already defined as case class Foo1
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
^
因此,您需要为您的特征使用与您的案例类不同的名称,这是明智的,因为它们指的是不同的东西。
重命名后,有几种方法可以实现您的目标。 一个由Sascha概述(使用类型成员),或者您可以提升类型T
来参数化您的特征,而不是在特征上参数化方法。
case class Foo1(i: Int)
trait Bar[T] {
def testFoo(l1 : List[T] ) : List[T]
}
implicit val foodVal: Bar[Foo1] = new Bar[Foo1] {
override def testFoo(l: List[Foo1]): List[Foo1] = {
for { a <- l if a.i == 1 } yield a
}
}
要回答更新问题,答案是声明和实现之间的区别。
当我在下面的表单中编写参数化特征的实现时,我说“用具体类型Foo
替换抽象类型T
”。
trait Bar[T] {...}
val instance = new Foo[Bar] { ... }
但是当我覆盖一个按类型参数化并更改类型名称的方法时,我所做的就是为该抽象类型指定一个不同的名称。 我没有指定要使用的具体具体类,我正在指定一个新符号来标识我正在操作的抽象类型。
trait Bar {
def something[T](t: T) : T
}
val instance = new Bar {
def something[Foo](t: Foo) : Foo = { ... }
}
因此,在上面的示例中, Foo
不是具体类型Foo
,而Foo
只是一个符号,表示将根据传递给方法的参数类型具体化的任意类型。
因此,即使在同一范围内存在一个名为Foo
的具体类型,它也会被引入的类型参数Foo
遮蔽,并且在该方法的上下文中对Foo
或Foo
实例的任何引用都是引用抽象类型参数Foo
,而不是具体类型Foo
。
您必须将特征和案例类命名为彼此不同,因为两种类型不能具有相同的名称。 (Mike已经解释过,为什么它在REPL中编译)
在行def testFoo[Foo1](l: List[Foo1]) = {
, Foo1
是一个新的泛型类型参数,而不是您的案例类 Foo1
的类型。
(恕我直言)用类型注释隐式val总是一个好主意。
也就是说,这里是应该做的代码,你想要的:
case class Foo1(i: Int)
trait Foo2 {
type T
def testFoo(l1 : List[T] ) : List[T]
}
implicit val foodVal: Foo2 = new Foo2 {
type T = Foo1
def testFoo(l: List[T]) = {
for { a <- l if a.i == 1 } yield a
}
}
更新(回答问题更新):
类型参数,尤其是命名参数,与常用参数非常相似。 是否访问类参数或具有相同名称的函数参数的关键是范围 。
class Foo(arg: Int) {
val field: Int = 1
def func(field: String) = {
// here field will always refer to the function paramter
// if you want to access the class field you would have to call this.field
}
}
class Bar[T] {
def func[T](t: T) = {
// within the scope of the function T IS NOT "Bar#T".
// However, you cannot access the class type parameter with "this.T" in this example
}
def func2(t: T) = {
// here T is the classes type parameter T
}
}
要通过this.
完成访问示例this.
class Bar[T] {
type Inner = T
def func[Inner](t: Inner) = {
// won't compile because the "Inner" type of the class is initialized with T
val test: this.Inner = t
}
}
如果你没有进一步指定一个类型参数,它可以是Any
(thing)。 如果您需要具有特定API的类型。 您需要使用<:
运算符告诉编译器。
trait Api {
def func: String
}
def func[T <: Api](arg: T) = {
arg.func // yay
}
这称为上限类型 。 有许多任意复杂的方法来细化类型参数,您必须阅读文档。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.