Scala:从子类动态访问伴随对象

时间:2016-02-03 20:26:47

标签: scala

当我没有实例时,我想要一些带有伴随对象的类,以便从类实例和伴随对象访问共享值。

最终我想解析一个字符串并创建匹配类的实例,反之亦然,从一个实例创建一个字符串。

以下代码有效,但似乎过于复杂:

abstract class Person(val age: Int = 0) {
    val pronoun: String
    override def toString = pronoun + " is " + age + " years old"
}

class Man(override val age: Int) extends Person {
    val pronoun = Man.pronoun
}

class Woman(override val age: Int) extends Person {
    val pronoun = Woman.pronoun
}

object Man extends Person {
    val pronoun = "he"
}

object Woman extends Person {
    val pronoun = "she"
}

object Person {
    def fromString(pronoun: String, age: Int): Option[Person] = {
        pronoun match {
            case Man.pronoun => Some(new Man(age))
            case Woman.pronoun => Some(new Woman(age))
            case _ => None
        }
    }
}

// Man.pronoun
// -> should return "he"

// Woman.pronoun
// -> should return "she"

// new Man(30).toString
// -> should return "he is 30 years old"

// Person.fromString("she", 20)
// -> should return a Some(Woman) with age = 20

因此,为此,我为每个子类创建一个类和一个伴随对象,并从伴随对象手动引用pronoun(例如val pronoun = Man.pronoun)。

旁注:在抽象类中设置默认值age = 0会使事情变得更容易一些,但看起来不对吗?!

我尝试过使用特征等但都没有成功。有没有更好的方法来实现这一点,重复性较低的代码?

2 个答案:

答案 0 :(得分:1)

您必须遵循哪些限制条件?例如。你必须能够调用Man.pronoun吗?因为一个选项是简单地在ManPronoun对象中创建WomanPronounPerson字段,然后移除ManWoman个随播广告对象。另外,0默认年龄值的重点是什么?您只需将年龄传递给Person

abstract class Person(val age: Int) {
  val pronoun: String
  override def toString = pronoun + " is " + age + " years old"
}

class Man(override val age: Int) extends Person(age) {
  val pronoun = Person.ManPronoun 
}

class Woman(override val age: Int) extends Person(age) {
  val pronoun = Person.WomanPronoun
}  

object Person {

  val ManPronoun = "he"
  val WomanPronoun = "she"

  def fromString(pronoun: String, age: Int): Option[Person] = {
    pronoun match {
      case ManPronoun   => Some(new Man(age))
      case WomanPronoun => Some(new Woman(age))
      case _            => None
    }
  }
}

如果您必须在ManWoman对象中包含代词字段,那么您必须拥有ManWoman个对象。 :)

答案 1 :(得分:1)

考虑到这个问题,我会通过以下方式简化它:

case class Person(pronoun: String, age: Int) {
  override def toString = s"$pronoun is $age years old"
}

这应该解决它,但我想你提出的问题是一个更复杂的问题的简化,所以我的解决方案变成了:

trait Person {
  protected def age: Int
  protected def pronoun: String
  override def toString = s"$pronoun is $age years old"
}

case class Man(override val age: Int) extends Person {
  override protected def pronoun: String = Person.SHE
}
case class Woman(override val age: Int) extends Person {
  override protected def pronoun: String = Person.HE
}

object Person {

  val HE = "he"
  val SHE = "she"

  def parse(pronoun: String, age: Int): Option[Person] = {
    pronoun match {
      case HE => Some(Man(age))
      case SHE => Some(Woman(age))
      case _ => throw new IllegalArgumentException(s"pronoun $pronoun was not recognized")
    }
  }

}

这对我来说看起来不太好,解决问题并且非常易读。 话虽如此,你的代码看起来并不错(0默认值)。这看起来很复杂,但我花了2秒钟来了解你在做什么,而且可读性在这些日子里非常重要。

希望这会对你有所帮助。此致!