[英]implementing a trait with a type parameter def fails to compile
I think I may be doing something very silly with this but following snippet doesn't compile. 我想我可能会做一些非常愚蠢的事情,但是下面的片段不能编译。
scala> case class Foo1(i: Int)
defined class Foo1
scala> trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
defined trait Foo1
And then implementing a trait as follow 然后实现如下特征
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
Update Thanks for the responses. 更新感谢您的回复。 I am genuinely interested in understanding difference between passing a type at a trait
level vs def
level. 我真的很想知道在trait
级别和def
级别之间传递类型之间的区别。 in the answers I can see type is passed at a trait level which does work but following still gives me the same error 在答案中,我可以看到类型是在一个特征级别传递,它确实有效,但后面仍然给我相同的错误
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
At a REPL, the definition: 在REPL,定义:
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
is replacing the class definition 正在取代类定义
case class Foo1(i: Int)
If you try to do the same thing using the paste command you will see that both cannot be defined together: 如果您尝试使用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] }
^
As such you need to use a different name for your trait than your case class, which is sensible, as they refer to different things. 因此,您需要为您的特征使用与您的案例类不同的名称,这是明智的,因为它们指的是不同的东西。
Once renamed, there are a couple of ways to achieve your goals. 重命名后,有几种方法可以实现您的目标。 One is outlined by Sascha (use a type member), or you can promote type T
to parameterize your trait instead of parameterizing the method on the trait. 一个由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
}
}
To answer the Update question, the answer is the difference between declaration and implementation. 要回答更新问题,答案是声明和实现之间的区别。
When I write an implementation of a parameterized trait in the form below, I am saying "replace the abstract type T
with the concrete type Foo
". 当我在下面的表单中编写参数化特征的实现时,我说“用具体类型Foo
替换抽象类型T
”。
trait Bar[T] {...}
val instance = new Foo[Bar] { ... }
But when I override a method that is parameterized by type and change the name of the type, all I am doing is specifying a different name for that abstract type. 但是当我覆盖一个按类型参数化并更改类型名称的方法时,我所做的就是为该抽象类型指定一个不同的名称。 I'm not specifying the specific concrete class to use, I'm specifying a new symbol by which to identify the abstract type that I am operating on. 我没有指定要使用的具体具体类,我正在指定一个新符号来标识我正在操作的抽象类型。
trait Bar {
def something[T](t: T) : T
}
val instance = new Bar {
def something[Foo](t: Foo) : Foo = { ... }
}
So, in the example above, Foo
is not a concrete type Foo
, rather Foo
is just a symbol that means an arbitrary type which will be made concrete based on the type of parameter passed to the method. 因此,在上面的示例中, Foo
不是具体类型Foo
,而Foo
只是一个符号,表示将根据传递给方法的参数类型具体化的任意类型。
As such, even if there is a concrete type called Foo
in the same scope, it is shadowed by the type parameter Foo
that has been introduced and any reference to Foo
, or an instance of Foo
, within the context of that method, is a reference to the abstract type parameter Foo
, not the concrete type Foo
. 因此,即使在同一范围内存在一个名为Foo
的具体类型,它也会被引入的类型参数Foo
遮蔽,并且在该方法的上下文中对Foo
或Foo
实例的任何引用都是引用抽象类型参数Foo
,而不是具体类型Foo
。
You have to name the trait and the case class differently form each other as two types cannot have the same name. 您必须将特征和案例类命名为彼此不同,因为两种类型不能具有相同的名称。 (Mike already explained, why it compiles in REPL) (Mike已经解释过,为什么它在REPL中编译)
In the line def testFoo[Foo1](l: List[Foo1]) = {
, Foo1
is a new generic type parameter and NOT the type of your case class Foo1
. 在行def testFoo[Foo1](l: List[Foo1]) = {
, Foo1
是一个新的泛型类型参数,而不是您的案例类 Foo1
的类型。
(IMHO) It is always a good idea to annotate an implicit val with a type. (恕我直言)用类型注释隐式val总是一个好主意。
That said, here is code that should do, what you want: 也就是说,这里是应该做的代码,你想要的:
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
}
}
Update (in answer to the questions Update): 更新(回答问题更新):
Type parameters, especially in naming, work very similar to usual parameters. 类型参数,尤其是命名参数,与常用参数非常相似。 The key to whether a class parameter is accessed or a identically named function parameter is the scope . 是否访问类参数或具有相同名称的函数参数的关键是范围 。
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
}
}
To complete the example with access by this.
要通过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
}
}
If you do not specify a type parameter further it can be Any
(thing). 如果你没有进一步指定一个类型参数,它可以是Any
(thing)。 If you need a type with a specific API. 如果您需要具有特定API的类型。 You need to tell the compiler that with the <:
operator. 您需要使用<:
运算符告诉编译器。
trait Api {
def func: String
}
def func[T <: Api](arg: T) = {
arg.func // yay
}
This is called an upper type bound . 这称为上限类型 。 There are more ways of arbitrary complexity to refine a type parameter, for which you will have to read the documentation. 有许多任意复杂的方法来细化类型参数,您必须阅读文档。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.