问题描述
trait Ingredient[T] {
def foo(t: T): Unit = {
// Some complex logic
}
}
以及我想要方法的类型:
class Cheese
class Pepperoni
class Oregano
def foo(t: Cheese)
def foo(t: Pepperoni)
def foo(t: Oregano)
不复制代码?以下将不起作用,因为它多次从同一特征中非法继承:
trait Pizza extends Ingredient[Cheese] with Ingredient[Pepperoni] with Ingredient[Oregano] {}
解决方法
我将提供两种解决方案:
-
如果您可以将
Cheese
、Pepperoni
和Oregano
定义为特征,您可以这样做:trait Ingredient { def foo[T <: Ingredient](t: T): Unit = { println(t) } }
然后扩展它:
trait Cheese extends Ingredient { override def toString: String = "Cheese" } trait Pepperoni extends Ingredient { override def toString: String = "Pepperoni" } trait Oregano extends Ingredient { override def toString: String = "Oregano" }
用法是:
trait Pizza extends Ingredient val pizza = new Pizza { } pizza.foo(new Cheese { }) pizza.foo(new Pepperoni { }) pizza.foo(new Oregano { })
代码在 Scastie 运行。
-
使用密封特性。这种方法将成分与问题中绑定的最终产品分开:
sealed trait Ingredient sealed trait PizzaIngredient extends Ingredient case object Cheese extends PizzaIngredient case object Pepperoni extends PizzaIngredient case object Oregano extends PizzaIngredient case object Cucumber extends Ingredient
然后定义
Pizza
特征:trait Pizza { def foo[T <: PizzaIngredient](t: T): Unit = { println(t) } }
用法是:
val pizza = new Pizza { } pizza.foo(Cheese) pizza.foo(Pepperoni) pizza.foo(Oregano)
代码运行于 Scastie
解决方案是创建扩展 Ingredient
trait 中的 Pizza
trait 的对象。这样我们就有了:
trait Pizza {
object CheeseIngredient extends Ingredient[Cheese]
object PepperoniIngredient extends Ingredient[Pepperoni]
object OreganoIngredient extends Ingredient[Oregano]
}
然后我们可以在继承的对象上调用我们的方法:
object LargePizza extends Pizza {
def bar(cheese: Cheese,pepperoni: Pepperoni,oregano: Oregano): Unit = {
CheeseIngredient.foo(cheese)
PepperoniIngredient.foo(pepperoni)
OreganoIngredient.foo(oregano)
}
}
或者,如果我们在 Ingredient
trait 中只有几个方法,我们可以创建重载并封装我们的支持对象:
trait Pizza {
private object CheeseIngredient extends Ingredient[Cheese]
private object PepperoniIngredient extends Ingredient[Pepperoni]
private object OreganoIngredient extends Ingredient[Oregano]
def foo(cheese: Cheese): Unit = CheeseIngredient.foo(cheese)
def foo(pepperoni: Pepperoni): Unit = PepperoniIngredient.foo(pepperoni)
def foo(oregano: Oregano): Unit = OreganoIngredient.foo(oregano)
}
object LargePizza extends Pizza {
def bar(cheese: Cheese,oregano: Oregano): Unit = {
foo(cheese)
foo(pepperoni)
foo(oregano)
}
}
同样可以通过在 LargePizza
中使用导入来实现。添加重载更好,因为客户端不需要编写额外的导入并且在范围内没有支持对象。使用重载的另一个好处是可以在子类中覆盖方法。
如果意图是 Pizza
需要所有这三种成分,那怎么样
class Pizza extends Ingredient[(Cheese,Pepperoni,Oregano)] {
def foo(ingredients: (Cheese,Oregano)) = {
case (cheese,pepperoni,oregano) =>
// do something with them
}
}
,
也许您可以执行以下操作,您必须更改 foo 方法:
trait Ingredient[T] {
def foo(ingredients: T*): Unit = {
for (i <- ingredients) {
// some complex logic
}
}
}
class Cheese
class Pepperoni
class Oregano
trait Pizza extends Ingredient[(Cheese,Oregano)]