我想创建一个抽象类,并能够向其中添加引用实现类的伴随对象的属性的成员。像这样(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
很抱歉,如果这是一个愚蠢的问题,或者之前被问过(我对如何说出这个问题没有信心,所以我不能轻易地搜索它)。提前谢谢!
答案 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
实际返回同伴对象,但您并不真正需要它。