繁体   English   中英

使用类型参数def实现特征无法编译

[英]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遮蔽,并且在该方法的上下文中对FooFoo实例的任何引用都是引用抽象类型参数Foo ,而不是具体类型Foo

  1. 您必须将特征案例类命名为彼​​此不同,因为两种类型不能具有相同的名称。 (Mike已经解释过,为什么它在REPL中编译)

  2. 在行def testFoo[Foo1](l: List[Foo1]) = {Foo1是一个新的泛型类型参数,而不是您的案例类 Foo1的类型。

  3. (恕我直言)用类型注释隐式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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM