Scala 类型参数化或结构子类型化或
你好! 我是scala的新手,因此在开发过程中提出了以下问题: 我想描述类树[T],其中T是类型参数。 但是T应该受到约束-它应该有两种方法: def key():A,其中A是某种类型,派生自方法的实现(!) 和def union(x:T):T,其中T与类型参数相同。 我想这个约束可以用几种方式表达:Scala 类型参数化或结构子类型化或,scala,Scala,你好! 我是scala的新手,因此在开发过程中提出了以下问题: 我想描述类树[T],其中T是类型参数。 但是T应该受到约束-它应该有两种方法: def key():A,其中A是某种类型,派生自方法的实现(!) 和def union(x:T):T,其中T与类型参数相同。 我想这个约束可以用几种方式表达: 定义两个特征,一个是使用方法键,另一个是使用方法联合(两个特征是由于这些方法的独立性) 使用结构子类型 还有别的 那么,我怎样才能做到这一点呢?还有其他方式吗 另外,如果可以很容易地为简单类型(如
另外,如果可以很容易地为简单类型(如String、Int等)添加这些方法,这也会很好。结构类型是用Java反射实现的,因此它们会降低性能。对于像脚本一样的短程序,或者对于初始化来说,这都是可以的,但是任何密集的使用都可能会使程序崩溃 所以我会选择你的第一个选择。您至少需要两个类型参数。但如果您不需要更精细的粒度,您只能选择一个特性:
trait Tree[T,A] {
def key(): A
def union( x: T ): T
}
如果您希望能够将这些方法“添加”到简单类型中,那么最好使用类型类,看看Kevin Wright的答案,它展示了如何“添加”一个
zero
和一个append
方法到Int
和String
您可以为键定义一个结构类型,但不能为联合定义结构类型。结构类型不能引用在其外部定义的抽象类型。所以这是行不通的:
trait Tree[T <: { def union(x: T): T }]
这有两种用途。首先,类必须实现该接口,这严重限制了哪些类可以用作键。这将是这样的:
trait Tree[T <: TreeVal[T]]
class IntVal(v: Int) extends TreeVal[Int] {
type A = Int
def key: A = v
def union(x: Int): Int = x + v
}
implicit def IntIsVal(v: Int): IntVal = new IntVal(v)
class Tree[T <% TreeVal[T]] // must be class, so it can receive parameters
implicit object IntVal extends TreeVal[Int] {
type A = Int
def key(v: Int) = v
def union(x: Int, y: Int) = x + y
}
class Tree[T: TreeVal](node: T, left: Option[Tree[T]], right: Option[Tree[T]]) {
val treeVal = implicitly[TreeVal[T]]
import treeVal._
override def toString = "(%s < %s > %s)" format (left.getOrElse("o"), key(node), right.getOrElse("o"))
}
或者,您可以将其与类型类模式一起使用,只需做一些更改:
trait TreeVal[T] {
type A
def key(v: T): A
def union(x: T, y: T): T
}
class Tree[T : TreeVal] // must be class, so it can receive parameters
类型类模式使用上下文边界。更多信息,请查阅。如今,这种风格比前一种风格更受欢迎,因为它在许多方面都更灵活。尽管如此,两者都会起作用
在这种情况下,可以这样使用:
trait Tree[T <: TreeVal[T]]
class IntVal(v: Int) extends TreeVal[Int] {
type A = Int
def key: A = v
def union(x: Int): Int = x + v
}
implicit def IntIsVal(v: Int): IntVal = new IntVal(v)
class Tree[T <% TreeVal[T]] // must be class, so it can receive parameters
implicit object IntVal extends TreeVal[Int] {
type A = Int
def key(v: Int) = v
def union(x: Int, y: Int) = x + y
}
class Tree[T: TreeVal](node: T, left: Option[Tree[T]], right: Option[Tree[T]]) {
val treeVal = implicitly[TreeVal[T]]
import treeVal._
override def toString = "(%s < %s > %s)" format (left.getOrElse("o"), key(node), right.getOrElse("o"))
}
隐式对象IntVal扩展了TreeVal[Int]{
类型A=Int
定义键(v:Int)=v
def接头(x:Int,y:Int)=x+y
}
类树[T:TreeVal](节点:T,左:选项[Tree[T]],右:选项[Tree[T]]){
val treeVal=隐式[treeVal[T]]
进口特雷瓦尔_
override def toString=“(%s<%s>%s)”格式(左.getOrElse(“o”)、键(节点)、右.getOrElse(“o”))
}
听起来他希望在T
上定义方法,而不是在树上定义方法。但还有一个问题:我为什么要在隐式对象IntVal
中显式指定类型A
?它可以从定义中推断出来吗?也许你可以使用参数化版本的方法key
:def key[A]:A
@incrop,我试过了,但是我仍然需要显式地写类型名,比如覆盖def key[String]=…
,因为在其他情况下会出现两个不同的key
方法(一个摘要,没有定义),我不明白,为什么A型不是inferred@newf嗯,key
必须有一个返回类型。因为它的定义是抽象的,Scala可以推断出它是什么类型。而且因为你只是说“a是某种类型”,我做得再好不过了。@newf顺便说一句,def key[String]
并不意味着key
的类型参数将是String
——您不能将参数化方法重写为非参数化。该def的作用与def key[T]相同
,但是调用参数String
而不是T
--String
将是参数,而不是类字符串
。