如何在 Scala 中多次继承泛型特征?
How to inherit generic trait multiple times in Scala?
我有这样的特质:
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] {}
解决方案是创建在 Pizza
特性中扩展 Ingredient
特性的对象。这样我们就有了:
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 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, pepperoni: Pepperoni, oregano: Oregano): Unit = {
foo(cheese)
foo(pepperoni)
foo(oregano)
}
}
同样可以通过在 LargePizza
中使用导入来实现。添加重载更好,因为客户端不需要编写额外的导入并且在范围内没有支持对象。使用重载的另一个好处是可以在子 类.
中覆盖这些方法
我将提供 2 个解决方案:
如果您可以将 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
也许您可以执行以下操作,但您必须更改 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,Pepperoni,Oregano)]
如果 Pizza
需要所有这三种成分,
怎么样?
class Pizza extends Ingredient[(Cheese, Pepperoni, Oregano)] {
def foo(ingredients: (Cheese, Pepperoni, Oregano)) = {
case (cheese, pepperoni, oregano) =>
// do something with them
}
}
我有这样的特质:
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] {}
解决方案是创建在 Pizza
特性中扩展 Ingredient
特性的对象。这样我们就有了:
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 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, pepperoni: Pepperoni, oregano: Oregano): Unit = {
foo(cheese)
foo(pepperoni)
foo(oregano)
}
}
同样可以通过在 LargePizza
中使用导入来实现。添加重载更好,因为客户端不需要编写额外的导入并且在范围内没有支持对象。使用重载的另一个好处是可以在子 类.
我将提供 2 个解决方案:
如果您可以将
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
也许您可以执行以下操作,但您必须更改 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,Pepperoni,Oregano)]
如果 Pizza
需要所有这三种成分,
class Pizza extends Ingredient[(Cheese, Pepperoni, Oregano)] {
def foo(ingredients: (Cheese, Pepperoni, Oregano)) = {
case (cheese, pepperoni, oregano) =>
// do something with them
}
}