我有两个案例类 Person 和 Employee
case class Person(identifier: String) {}
case class Employee (salary: Long) extends Person {}
我遇到以下错误:
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
我是Scala的新手,无法理解我该怎么做。
版本: 斯卡拉:2.11
答案 0 :(得分:3)
不幸的是,恐怕案例类无法扩展另一个案例类。
“普通”类中的继承如下所示:
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"
如果您想通过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"
定义提取器
Extractor提供了一种定义模式匹配中使用的匹配语句的方法。在object
方法中的unaply
中定义。
让我们再次考虑第一个示例,它增加了对提取器的支持:
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
}
}
}
现在,让我们定义一个使用这些提取器定义模式匹配的方法:
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...
答案 1 :(得分:2)
从案例类继承(即使使用常规的非案例类,这也是不被禁止的)是一个坏主意。查看this answer,了解原因。
您Person
不必是案例类。实际上,它根本不需要是一个类:
trait Person {
def identifier: String
}
case class Employee(identifier: String, salary: Long) extends Person
答案 2 :(得分:2)
Scala中的案例类添加了几个不同的功能,但是通常您实际上只使用其中的一些功能。因此,您需要回答的主要问题是您真正需要哪些功能。以下是基于the spec的列表:
val
new
方法来消除对apply
的需求unapply
方法添加到伴随对象来支持模式匹配。 (Scala的优点之一是模式匹配是通过非魔术方式完成的,您可以将其实现为任何数据类型,而无需将其设为case class
)equals
和hashCode
实现toString
个实现copy
方法(之所以有用,是因为case class
在默认情况下是不可变的)Product
特性 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) {}
实际上,反对从case class
继承(尤其是使case class
继承另一个)的主要反对意见是Product
特性copy
和equals
/ { {1}},因为它们会引起歧义。添加hashCode
可以部分缓解最后一个问题,但不能缓解第一个问题。另一方面,在像您这样的层次结构中,您可能根本不需要canEqual
方法或copy
实现。如果在模式匹配中不使用Product
,则也不需要Person
。您可能unapply
真正需要的全部是case
,apply
和toString
/ hashCode
/ equals
。