4

我很难从 C++/模板世界过渡到 scala。我习惯于对我想要的模板参数 T 使用任何操作,只要我用来实例化 T 的任何东西都支持这些操作(基本上是编译时 Duck 类型)。我在 Scala 中找不到相应的习惯用法,它允许我定义一个具有单个类型参数的抽象类,并且它需要类型 T 的某个接口。

我几乎可以工作,但我无法弄清楚如何告诉抽象类(Texture[T <: Summable[T]])T 支持从 Int 进行转换/构造。如何将隐式转换添加到特征 Summable 以便 Texture 知道 T 支持转换?

trait Summable[T] { 
   def += (v : T) : Unit
   def -= (v : T) : Unit   
}

object Int4 { implicit def int2Int4(i : Int) = new Int4(i, i, i, i) }

class Int4 (var x : Int, var y : Int, var z : Int, var w : Int) extends Summable[Int4] {
   def this (v : Int) = this(v, v, v, v)
   def += (v : Int4) : Unit = { x += v.x; y += v.y; z += v.z; w += v.w }
   def -= (v : Int4) : Unit = { x -= v.x; y -= v.y; z -= v.z; w -= v.w } 
}

abstract class Texture[Texel <: Summable[Texel]] {
   var counter : Texel
   def accumulate(v : Texel) : Unit = { counter += v }
   def decrement() : Unit = { counter -= 1 } //< COMPILE ERROR HERE, fails to find implicit
}

class Int4Target extends Texture[Int4] {
   var counter : Int4 = new Int4(0, 1, 2, 3)
}
4

3 回答 3

4

您可以像这样定义一个隐式构造函数参数

abstract class Texture[Texel <: Summable[Texel]](implicit int2Texel: Int => Texel) {
//...

这实质上是告诉编译器,为了构造 的实例Texture,必须有一个隐式转换函数可用于Intto Texel。假设您在范围内的某处定义了这样的函数(您这样做了),您应该不再收到编译错误。

Edit2:好的,我最初误读了您的代码,实际上您只需要一个隐式参数 from Int => Texel。您的代码通过上述修改为我编译。

编辑:您实际上需要 2 个转换函数,一个 fromTexel => Int和另一个 fromInt => Texel以便正确重新分配 var

于 2012-02-12T02:41:42.677 回答
3

C++ 模板和 Scala 中的任何东西之间的根本区别在于,C++ 模板是为每次使用而编译的——也就是说,如果你使用带有int和 with的模板double,那么会编译两个不同的类,并且它们仅在某些代码实际生成时才编译使用它。

另一方面,Scala 有单独的编译。考虑到 JVM 的限制,不如 Java 的好,但仍然遵循基本原则。因此,如果某物有类型参数,它仍然在声明时编译,并且只有一个这样的类存在。编译后的代码必须支持所有可能的参数,而不是可以调用的参数,这使得限制与模板完全不同。

关于特征和隐式转换,特征不支持参数,隐式转换(视图边界)是参数。相反,使用一个类。

于 2012-02-12T07:12:48.723 回答
2

在 scala 中,不可能要求 trait 的类型参数存在隐式转换。这是有充分理由的。假设我们定义了一个像这样的特征:

trait ATrait[T <% Int] {
    def method(v: T) { println(v: Int) }
}

然后在两个地方做了实例:

package place1 {
    implicit def strToInt(s: String) = 5
    val inst = new ATrait[String]
}

package place2 {
    implicit def strToInt(s: String) = 6
    val inst = new ATrait[String]
}

然后使用这些实例,例如:

val a = if (someTest) place1 else place2
a.method("Hello")

这应该打印5还是6?也就是说,它应该使用哪种隐式转换?必须在编译时找到隐式,但您不知道创建对象时存在哪个隐式转换。

换句话说,隐式是由使用它们的范围提供的,而不是由使用它们的对象提供的;后者是不可能的。

所以,关于你的问题。您可以使用普通成员,而不是使用隐式成员:

trait Summable[T] { 
   def -= (v: T): Unit
   def -= (v: Int) { this -= (encode(v)) }

   def encode(i: Int): T
}

class Int4 (var x: Int, var y: Int, var z: Int, var w: Int) extends Summable[Int4] {
   def -= (v : Int4) : Unit = { x -= v.x; y -= v.y; z -= v.z; w -= v.w } 

   def encode(i: Int) = Int4.int2Int4(i)
}

现在该decrement方法可以正确编译。

另一种说法是,不要将隐式视为属于某个类型的属性(即,“可以从 Int 隐式转换”不是 Int4 的属性)。它们是值,可以使用类型来标识。

希望这可以帮助。

于 2012-02-12T02:25:34.100 回答