Scala:要求成员使用抽象类的伴随对象

时间:2017-07-04 19:58:47

标签: scala

我想创建一个抽象类,并能够向其中添加引用实现类的伴随对象的属性的成员。像这样(Scala伪代码):

abstract class Fruit(cultivar: String) {
    // How do I reference the implementing class's companion object here?
    def isTastyCultivar(): Boolean = Fruit.tastyCultivars.contains(cultivar)
}

// how do I implement what I am thinking of as "the abstract companion object"
abstract object Fruit {
    val tastyCultivars: Set[String]  // must be implemented
                                     // by the concrete object
}

class Apple(cultivar: String) extends Fruit(cultivar) {

}

object Apple extends Fruit{ // presumably this is not correct; 
                            // it needs to extend the FruitObject
                            // or whatever it is
    val tastyCultivars: Set[String] = Set("Red Delicious", "Granny Smith")
}

class Tomato(cultivar: String) extends Fruit(cultivar) {

}

object Tomato extends Fruit{
    val tastyCultivars = Set("Roma")
}

val a1 = new Apple("Red Delicious")
val a2 = new Apple("Honeycrisp")

a1.isTastyCultivar()  // should return true
a2.isTastyCultivar()  // should return false

val t1 = new Tomato("Roma")
val t2 = new Tomato("San Marzano")

t1.isTastyCultivar() // should return true
t2.isTastyCultivar() // should return false

很抱歉,如果这是一个愚蠢的问题,或者之前被问过(我对如何说出这个问题没有信心,所以我不能轻易地搜索它)。提前谢谢!

4 个答案:

答案 0 :(得分:1)

一种可能的解决方案是使用type class pattern。我们通过ADT获得域模型(或代数):

sealed trait Fruit
case class Apple()
case class Orange()

我们有类型类,它定义了我们想要提供的结构:

trait TastyCultivarSupplier[T <: Fruit] {
  def tastyCultivars: Set[String]
}

现在每种具有美味品种的类型都需要实现类型类才能提供它们。一种可行的方法是在配套对象中实现类型类:

object Apple {
  implicit def appleTastyCultivars = new TastyCultivarSupplier[Apple] {
    override def tastyCultivars: Set[String] = Set("Yummy stuff")
  }
}

在消费者或想要获得美味品种的类型中,我们需要TastyCultivarSupplier的隐含证据:

class TastyCultivarConsumer {
  def isTasty[T: TastyCultivarSupplier](name: String): Boolean =
    implicitly[TastyCultivarSupplier[T]].tastyCultivars.contains(name)
}

当调用isTasty时,它需要在范围内有一个供应商,否则将发生编译时错误:

def main(args: Array[String]): Unit = {
  println(new TastyCultivarConsumer().isTasty("Yummy stuff"))
}

会给我们:

Error:(33, 48) could not find implicit value for evidence parameter
of type TastyCultivarSupplier[T]
    println(new TastyCultivarConsumer().isTasty("Yummy stuff"))

要解决此问题,我们会导入我们想要的供应商:

def main(args: Array[String]): Unit = {
  import Apple._
  println(new TastyCultivarConsumer().isTasty("Yummy stuff"))
}

现在我们的代码编译了。请注意,实施者不必强制在同伴对象中写入证据,只要它在编译器的查找范围内,他就可以自由地在他想要的任何地方这样做。

答案 1 :(得分:0)

不可能做你要求的事。

您问的是object的覆盖成员。 scala中的object等同于Java中的静态类,所有成员都是静态的。 具有类的伴随对象将是Java中具有静态成员和非静态成员的类。 你无法覆盖它们。 You can't do it in Java并且您无法在Scala中执行此操作。

替代解决方案:

只需在课程中定义tastyCultivars

abstract class Fruit(cultivar: String) {
  val tastyCultivars: Set[String]
  def isTastyCultivar(): Boolean = tastyCultivars.contains(cultivar)
}

class Apple(cultivar: String) extends Fruit(cultivar) {

    val tastyCultivars: Set[String] = Set("Red Delicious", "Granny Smith")
}

class Tomato(cultivar: String) extends Fruit(cultivar) {

    val tastyCultivars = Set("Roma")
}

val a1 = new Apple("Red Delicious")
val a2 = new Apple("Honeycrisp")

println("Should return true, is " + a1.isTastyCultivar())
println("Should return false, is " +a2.isTastyCultivar())

val t1 = new Tomato("Roma")
val t2 = new Tomato("San Marzano")

println("Should return true, is " +t1.isTastyCultivar())
println("Should return false, is " +t2.isTastyCultivar())

输出:

$ scala fruit.scala 
Should return true, is true
Should return false, is false
Should return true, is true
Should return false, is false

答案 2 :(得分:0)

非常感谢Yuval教我类型学课程;我用他的技术编写了一个解决方案,满足了我原来的所有标准:

// Fruit instance members
abstract class FruitClass(cultivar: String) {
    def getCultivar: String = cultivar
}

// Obviously not really an object but serves the purpose of
// the thing I envisioned as the "abstract object" which is not a thing
// I.e., a place to put fruit static members
trait FruitObject[A <: FruitClass] {
    // Any subclass of fruit must (statically) specify their set of tasty cultivars...
    val tastyCultivars: Set[String]
    // ...but they can inherit the common implementation of isTastyCultivar()
    def isTastyCultivar(x: A): Boolean = tastyCultivars contains x.getCultivar
}

// Subclass #1: Apples
class Apple(cultivar: String) extends FruitClass(cultivar)
implicit object AppleIsFruit extends FruitObject[Apple] {
    val tastyCultivars = Set("Red Delicious", "Granny Smith")
}

// Subclass #2: Tomatoes
class Tomato(cultivar: String) extends FruitClass(cultivar)
implicit object TomatoIsFruit extends FruitObject[Tomato] {
    val tastyCultivars = Set("Roma")
}

def isTastyCultivar[A <: FruitClass: FruitObject](thing: A) =
    implicitly[FruitObject[A]].isTastyCultivar(thing)

isTastyCultivar(new Apple("Red Delicious"))    // true
isTastyCultivar(new Apple("Honeycrisp"))    // false
isTastyCultivar(new Tomato("Roma"))            // true
isTastyCultivar(new Tomato("San Marzano"))    // false

Gustek是完全正确的,对象成员不能被覆盖,但这种方法似乎达到了相同的效果,而没有实际使用Fruit的伴随对象来声明静态成员。

答案 3 :(得分:0)

一个简单的解决方案(一个Scala Collections use):只需添加一个将伴侣返回给该类的方法。

trait FruitCompanion {
  val tastyCultivars: Set[String]
}

abstract class Fruit(cultivar: String) {
  def companion: FruitCompanion
  def isTastyCultivar(): Boolean = companion.tastyCultivars.contains(cultivar)
}

class Apple(cultivar: String) extends Fruit(cultivar) {
  def companion = Apple
}

object Apple extends FruitCompanion {
  val tastyCultivars: Set[String] = Set("Red Delicious", "Granny Smith")
}

请注意,您无法强制companion实际返回同伴对象,但您并不真正需要它。