我对Scala编程非常陌生,我非常喜欢代码可组合的程度。我想编写一些处理两个可相互转换的相关对象的特性,并通过继续扩展该特征来构建更多功能,这样当我创建对象时,我可以为我的泛型指定相关类型。这是我正在谈论的代码类型的工作玩具示例:
trait FirstConverter[First] {
def toFirst: First
}
trait SecondConverter[Second] {
def toSecond: Second
}
trait TwoWayConverter[First <: SecondConverter[Second], Second <: FirstConverter[First]] {
def firstToSecond(x: First) = x.toSecond
def secondToFirst(x: Second) = x.toFirst
}
trait RoundTripConverter[First <: SecondConverter[Second], Second <: FirstConverter[First]] extends TwoWayConverter[First, Second] {
def firstToFirst(x: First) = secondToFirst(firstToSecond(x))
def secondToSecond(x: Second) = firstToSecond(secondToFirst(x))
}
case class A(s: String) extends SecondConverter[B] {
def toSecond: B = B((s.toInt) + 1)
}
case class B(i: Int) extends FirstConverter[A] {
def toFirst: A = A((i * 2).toString)
}
object ABConverter extends RoundTripConverter[A, B]
object Main {
def main(args: Array[String]): Unit = {
println(ABConverter firstToSecond A("10")) // 11
println(ABConverter secondToFirst B(42)) // 84
println(ABConverter firstToFirst A("1")) // 4
println(ABConverter secondToSecond B(2)) // 5
}
}
虽然这有效,但我不确定它是否是惯用的Scala。我问是否有任何技巧可以使类型定义更简洁,如果我能以某种方式定义类型限制只有一次,并让它们被多个特征使用,扩展其他特征。
提前致谢!
答案 0 :(得分:3)
改进设计的一种方法是使用类型类,而不是继承自SecondConverter
和A
。这样,您可以为相同类型使用多个转换函数,并在您无法控制的类之间进行转换。
一种方法是创建一个可以将B
转换为trait Converter[A, B] {
def convert(a: A): B
}
trait TwoWayConverter[A, B] {
def firstToSecond(a: A)(implicit conv: Converter[A, B]): B = conv.convert(a)
def secondToFirst(b: B)(implicit conv: Converter[B, A]): A = conv.convert(b)
}
trait RoundTripConverter[A, B] extends TwoWayConverter[A, B] {
def firstToFirst(a: A)(implicit convAB: Converter[A, B], convBA: Converter[B, A]) =
secondToFirst(firstToSecond(a))
def secondToSecond(b: B)(implicit convAB: Converter[A, B], convBA: Converter[B, A]) =
firstToSecond(secondToFirst(b))
}
的类型类:
Foo
我们可以为以下两个类Bar
和A
创建类似于B
和case class Foo(s: String)
case class Bar(i: Int)
implicit val convFooBarFoor = new Converter[Foo, Bar] {
def convert(foo: Foo) = Bar((foo.s toInt) + 1)
}
implicit val convBarFoo = new Converter[Bar, Foo] {
def convert(bar: Bar) = Foo((bar.i * 2) toString)
}
FooBarConverter
然后我们可以创建一个object FooBarConverter extends RoundTripConverter[Foo, Bar]
FooBarConverter firstToSecond Foo("10") // Bar(11)
FooBarConverter secondToFirst Bar(42) // Foo(84)
FooBarConverter firstToFirst Foo("1") // Foo(4)
FooBarConverter secondToSecond Bar(2) // Bar(5)
:
Converter
唯一的问题是因为我们无法将参数传递给特征,我们不能将类型限制为具有StringIntConverter
类型类实例的类型。因此,即使没有Converter[String, Int]
和/或Convert[Int, String]
个实例,您也可以在下面创建object StringIntConverter extends TwoWayConverter[String, Int]
。
StringIntConverter.firstToSecond("a")
您无法调用firstToSecond
,因为{{1}}方法需要两个提到的类型类实例的隐式证据。