如何在 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] {}

解决方法

我将提供两种解决方案:

  1. 如果您可以将 CheesePepperoniOregano 定义为特征,您可以这样做:

    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 运行。

  2. 使用密封特性。这种方法将成分与问题中绑定的最终产品分开:

    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)]

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...