Scala 禁止case class inheritance

case class Person(name: String, age: Int)
case class FootballPlayer(name: String, age: Int, number: Int) extends Person(name, age)

在编译时会报出以下错误:

Error:(5, 12) case class FootballPlayer has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
case class FootballPlayer(name: String, age: Int, number: Int) extends Person(name, age)
^

原因有挺多,下边的两篇文章讲了下理由,但我还是没明白其中的必要性。

http://tech.schmitztech.com/scala/caseclassinheritence.html

http://stackoverflow.com/questions/11158929/what-is-so-wrong-with-case-class-inheritance


我们可以使得两个case class继承同一个trait来实际于case class继承的行为,但是这样就得自己写extractor了。比如

  sealed trait Person{
def age: Int
def name: String
}
case class FootballPlayer(name: String, age: Int, number: Int) extends Person
val torres = FootballPlayer("Fernando Torres", 31, 19)
val Person(name, age) = torres // can't resolve symbal Person

会编译出错, 因为Person是个trait,它并不支持extractor的语法。

需要给Person加个Companion object,像这样

  object Person{
def unapply(p: Person) = Some((p.name, p.age))
}

这时就能用extractor了

  val torres = FootballPlayer("Fernando Torres", 31, 19)
val Person(name, age) = torres

编译器会为case class生成equals方法,但普通类就不会了。

  sealed trait Person{
def age: Int
def name: String
} case class FootballPlayer(name: String, age: Int, number: Int) extends Person
class Doctor(val name: String, val age: Int) extends Person
val torresA = FootballPlayer("Fernando Torres", 31, 19)
val torresB = FootballPlayer("Fernando Torres", 31, 19)
println(torresA == torresB)//true val docA = new Doctor("C", 30)
val docB = new Doctor("C", 30)
println(docA == docB)//false

这时,当两个FootballPlayer的构造参数相同,它们就相等。但是对于Doctor类来说不是这样了。

当给FootballPlayer这个case class的父类Person定义了equals方法之后,就不是这样了。

 sealed trait Person{self =>
def age: Int
def name: String
override def equals(that: Any):Boolean = {
that match{
case p: Person => p.age == self.age && p.name == self.name
case _ => false
}
}
override def hashCode: Int = {
var hash = 1
hash = hash * 31 + age
hash = hash * 31 + {if(name !=null) name.hashCode else 0}
hash
}
}
case class FootballPlayer(name: String, age: Int, number: Int) extends Person
class Doctor(val name: String, val age: Int) extends Person
val torresA = FootballPlayer("Fernando Torres", 31, 19)
val torresB = FootballPlayer("Fernando Torres", 31, 19)
torresA.equals(torresB)
println(torresA == torresB)//true val docA = new Doctor("C", 30)
val docB = new Doctor("C", 30)
println(docA == docB) //true val footballPlayerC = FootballPlayer("C", 30, 30)
println(footballPlayerC == docA) //true
println(footballPlayerC.hashCode())//
println(docA.hashCode())//

貌似这时候case class就不会生成equals方法了, 转而使用父类Person的equals方法。并且要记得同时在Person中覆盖hashCode方法,不然就破坏了equals对hashCode的要求。

case class inheritance的更多相关文章

  1. Three Sources of a Solid Object-Oriented Design

    pingback :http://java.sys-con.com/node/84633?page=0,1 Object-oriented design is like an alloy consis ...

  2. C#调用C++导出类(转)

    由于使用别人的Dll,导出的是一个实体类,在C#里封送很难,百度下,有个朋友回复一篇英文的,虽然不一定使用,但可以作为一个知识点,现把原文贴下: c#调用C++写的dll导出类,包含继承,重载等详细介 ...

  3. TFS 2015 Build Agent failing syncing the repository 获取源码 不全 失败

    当我们使用TFS2015d的生成代理时,我们将生成定义加入代理池队列中,但是代理可能无法完全下载我们在TFS代码浏览器中看到的所有目录,这肯定会导致编译失败呀!为什么呢? 原因在于tfscompile ...

  4. Ubuntu+apache安装redmin

    公司要迁移redmin,本来以为是一个很简单的项目,想不到整整搞了一天加一个晚上. 首先是对ruby的安装不熟悉,现在明白了ruby的安装顺序是先安装rvm版本管理,然后用rvm安装ruby,安装好后 ...

  5. The IDL compiler

    The IDL compiler or bindings generator transcompiles Web IDL to C++ code, specifically bindings betw ...

  6. C++: virtual inheritance and Cross Delegation

    Link1: Give an example Note: I think the Storable::Write method should also be pure virtual. http:// ...

  7. JavaScript Patterns 6.2 Expected Outcome When Using Classical Inheritance

    // the parent constructor function Parent(name) { this.name = name || 'Adam'; } // adding functional ...

  8. Effective Java 17 Design and document for inheritance or else prohibit it

    Principles The class must document its self-use of overridable methods. A class may have to provide ...

  9. Think Python - Chapter 18 - Inheritance

    In this chapter I present classes to represent playing cards, decks of cards, and poker hands.If you ...

随机推荐

  1. jQuery之锚点带动画跳转特效

    背景图片为金木研,这是我最爱的一张图. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" &quo ...

  2. 远程连接MySql连不上1130

    mysql数据库user表中已存在主机=%的用户root,连接却提示1130. 用navicat开启user表>>>>在表里直接修改%账户root的密码(看到是未加密过的内容) ...

  3. 【转】预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反)

    用VC++ 2008 编写C语言程序,编译出现错误: 预编译头文件来自编译器的早期版本,或者预编译头为 C++ 而在 C 中使用它(或相反) 解决方法: 建工程时 建立空项目 或者在项目设置里关闭预编 ...

  4. Cocos移植到Android-通过命令行交叉编译打包

    当我们在Windows下使用Visual Studio开发好Win32平台的游戏程序后,我们需要把它们移植到不同的平台上.Cocos2d-x支持很多个平台,然而,我们不可能介绍全部平台的移植.我们总结 ...

  5. Bootstrap两端对齐的导航实例

    Bootstrap两端对齐的导航,样式剥离出来代码如下: <!DOCTYPE html> <html> <head> <title>Bootstrap ...

  6. 引用类型之Array类型

    Array类型 ECMAScript数组与其它语言数组一样,都是数据的有序列表.但是ECMAScript数组的每一项可以保存任何类型的数据.而且,ECMAScript数组是可以动态调整的. 1.创建和 ...

  7. HOWTO re

    \w 字母数字字符 [a-z A-Z 0-9_] \W 非字母数组字符 [^a-z-A-Z 0-9_] \d 十进制数字 [0-9] \D 非数字字符 [^0-9] \s 空白字符 [\t\n\r\f ...

  8. [转]WINDOW进程间数据通讯以及共享内存

    1.引言 在Windows程序中,各个进程之间常常需要交换数据,进行数据通讯.WIN32 API提供了许多函数使我们能够方便高效地进行进程间的通讯,通过这些函数我们可以控制不同进程间的数据交换,就如同 ...

  9. 关于mysql group_concat 不能显示为空的列的其他信息

    今天做项目遇到一个问题,百度好久都没找到问题所在 是酱紫的,一张表 关联的表 然后我用sql语句查询 point.pid,point.pname,GROUP_CONCAT(downsite.pname ...

  10. Linux C 程序 文件属性,文件删除(15)

    dup ,dup2,fcntl,ioctl系统调用 . dup ,dup2 函数 int dup(int oldfd) int dup(int oldfd , int newfd) dup用来复制参数 ...