简体   繁体   English

从另一个案例类扩展案例类

[英]Extend case class from another case class

I have two case class Person and Employee 我有两个案例类PersonEmployee

case class Person(identifier: String) {}

case class Employee (salary: Long) extends Person {}

I am getting following error: 我收到以下错误:

Unspecified value parameters: identifier: String
Error: case class Employee has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes

I am new to Scala and not able to understand what I have to do. 我是Scala的新手,无法理解我的工作。

Version: Scala : 2.11 版本:Scala:2.11

Unfortunately, I'm afraid it is not possible for case class to extend another case class. 不幸的是,恐怕案例类不可能扩展另一个案例类。

The inheritance in "plain" classes would look like: “普通”类中的继承如下所示:

class Person(val identifier: String) {}

class Employee(override val identifier: String, salary: Long)
  extends Person(identifier) {}

val el = new Employee("abc-test", 999)
println(el.identifier) // => "abc-test"

If you would like to achieve a similar effect with case classes, you would need to reach out to trait s: 如果您希望通过case类实现类似的效果,则需要联系trait s:

trait Identifiable {
  def identifier: String
}

case class Person(identifier: String) extends Identifiable {}

case class Employee(identifier: String, salary: Long)
  extends Identifiable {}

val el = Employee("abc-test", 999)
println(el.identifier) // => "abc-test"

Defining extractors 定义提取器

Extractor provides a way for defining a matching statement used in pattern matching. 提取器提供了一种定义模式匹配中使用的匹配语句的方法。 It is defined in an object in unaply method. 它是通过unaply方法在object中定义的。

Let's consider the first example again adding support for extractors: 让我们考虑第一个示例,再次添加对提取器的支持:

class Person(val identifier: String)

class Employee(override val identifier: String, val salary: Long)
  extends Person(identifier)

object Person {
  def unapply(identifier: String): Option[Person] = {
    if (identifier.startsWith("PER-")) {
      Some(new Person(identifier))
    }
    else {
      None
    }
  }
}

object Employee {
  def unapply(identifier: String): Option[Employee] = {
    if (identifier.startsWith("EMP-")) {
      Some(new Employee(identifier, 999))
    }
    else {
      None
    }
  }
}

Now, let's define a method that will define pattern matching using those extractors: 现在,让我们定义一个使用这些提取器定义模式匹配的方法:

def process(anInput: String): Unit = {
  anInput match {
    case Employee(anEmployee) => println(s"Employee identified ${anEmployee.identifier}, $$${anEmployee.salary}")
    case Person(aPerson) => println(s"Person identified ${aPerson.identifier}")
    case _ => println("Was unable to identify anyone...")
  }
}

process("PER-123-test") // => Person identified PER-123-test
process("EMP-321-test") // => Employee identified EMP-321-test, $999
process("Foo-Bar-Test") // => Was unable to identify anyone...

Case classes in Scala add several different features but often you really use only some of them. Scala中的案例类添加了几个不同的功能,但通常您实际上只使用其中的一些功能。 So the main question you need to answer is which features you really need. 因此,您需要回答的主要问题是您真正需要哪些功能。 Here is a list based on the spec : 这是基于规格的列表:

  • remove the need to type val before field names/constructor params 无需在字段名称/构造函数参数之前键入val
  • remove the need for new by adding apply method to the companion object 通过将apply方法添加到伴随对象来消除对new对象的需求
  • support for pattern matching by adding unapply method to the companion object. 通过将unapply方法添加到伴随对象来支持模式匹配。 (One of nice things of Scala is that pattern-matching is done in a non-magical way, you can implement it for any data type without requiring it to be a case class ) (Scala的优点之一是模式匹配是通过非魔术方式完成的,您可以将其实现为任何数据类型,而无需将其作为case class
  • add equals and hashCode implementations based on all the fields 根据所有字段添加equalshashCode实现
  • add toString implementations 添加toString实现
  • add copy method (useful because case class es are immutable by default) 添加copy方法(因为case class在默认case class是不可变的,所以很有用)
  • implement Product trait 实现Product特征

A reasonable guess of the equivalent for case class Person(identifier: String) is case class Person(identifier: String)的等效猜测是

class Person(val identifier: String) extends Product  {

  def canEqual(other: Any): Boolean = other.isInstanceOf[Person]

  override def equals(other: Any): Boolean = other match {
    case that: Person => (that canEqual this) && identifier == that.identifier
    case _ => false
  }

  override def hashCode(): Int = identifier.hashCode

  override def toString = s"Person($identifier)"

  def copy(newIdentifier: String): Person = new Person(newIdentifier)

  override def productElement(n: Int): Any = n match {
    case 0 => identifier
    case _ => throw new IndexOutOfBoundsException(s"Index $n is out of range")
  }

  override def productArity: Int = 1
}

object Person {
  def apply(identifier: String): Person = new Person(identifier)

  def unapply(person: Person): Option[String] = if (person eq null) None else Some(person.identifier)
}

case class Employee(override val identifier: String, salary: Long) extends Person(identifier) {}

Actually the main objections to inheriting from a case class and especially making a case class inheriting another one are the Product trait, copy and equals / hashCode because they introduce ambiguity. 实际上,反对从一个case class继承,尤其是使一个case class继承另一个case class的主要反对意见是Product trait, copyequals / hashCode因为它们引入了歧义。 Adding canEqual partially mitigates the last problem but not the first ones. 添加canEqual部分缓解最后一个问题,但不能缓解第一个问题。 On the other hand in a hierarchy like yours, you probably don't need the copy method or Product implementation at all. 另一方面,在像您这样的层次结构中,您可能根本不需要copy方法或Product实现。 If you don't use Person in pattern matching, you don't need unapply as well. 如果在模式匹配中不使用“ Person ,则也不需要unapply Most probably all you really need case for is apply , toString and hashCode / equals / canEqual . 您可能真正需要的全部case就是applytoStringhashCode / equals / canEqual

Inheriting from case classes (even with regular non-case classes, which is not prohibited) is a bad idea. 从案例类继承(即使使用常规的非案例类,这也是不被禁止的)是一个坏主意。 Check this answer out to get an idea why. 查看此答案以了解原因。

You Person does not need to be a case class. 您的Person不需要是案例类。 It actually does not need to be a class at all: 实际上,它根本不需要是一个类:

trait Person {
   def identifier: String
}
case class Employee(identifier: String, salary: Long) extends Person

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM