scala(九) 封装、继承与多态
封装
封装就是把抽象出的数据和对数据的操作
封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作(成员方法),才能对数据进行操作。
java封装操作如下:
- 将属性进行私有化
- 提供一个公共的set方法,用于对属性赋值
- 提供一个公共的get方法,用于获取属性的值
定义一个对象
class Person{
/**
* id
*/
private var id:Int=_
/**
* 姓名
*/
private var name:String=_
/**
* 年龄
*/
private var age:Int=_
/**
* 性别
*/
private var sex:Char=_
}
在 java 中可以通过第三方工具快速生成 get/set方法
;在scalal中(idea)不行。若需要添加get/set方法
需要自己写。
如:
def getId():Int=this.id
def setId(id:Int)=this.id=id
在scala 为了符合JavaBean规范,专门提供了一个 注解 @BeanProperty
用于对属性生成 getter/setter 方法。
使用 @BeanProperty
是有限制的。
- 属性不能被
private
修饰 - 属性也不能声明成
val
class Person{
/**
* id
*/
@BeanProperty var id:Int=_
/**
* 姓名
*/
@BeanProperty var name:String=_
/**
* 年龄
*/
@BeanProperty var age:Int=_
/**
* 性别
*/
@BeanProperty var sex:Char=_
}
使用
def main(args: Array[String]): Unit = {
val p=new Person()
p.setId(1001)
p.setName("张三")
p.setAge(19)
p.setSex('男')
val info=s"id=${p.getId},姓名=${p.getName},年纪=${p.getAge},性别=${p.getSex}"
println(info) // id=1001,姓名=张三,年纪=19,性别=男
}
疑问?使用get/set 不就是用来访问和操作私有属性的吗? 使用 @BeanProperty
居然还必须时 public
那么定义该注解的有何用?
如下:不使用 get/set 可以进行操作。
def main(args: Array[String]): Unit = {
val p=new Person()
p.id=1001
p.name="张三"
p.age=19
p.sex='男'
val info=s"id=${p.id},姓名=${p.name},年纪=${p.age},性别=${p.sex}"
println(info) // id=1001,姓名=张三,年纪=19,性别=男
}
有没有小伙伴和我有一样的疑问?
@BeanProperty 只是用于符合JavaBean
规范,java很多api都遵循这个规范,scala若要去调用,也不得不去准寻这规范。严格意义上来说,scala的封装并不是封装。
还是用个案例来说明吧。json 可以用于将 JavaBean转为JSON或者将 JSON格式数据转为JavaBean。
导入 fastjson
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.72</version>
</dependency>
对象 转 json
def main(args: Array[String]): Unit = {
val p=new Person()
p.id=1001
p.name="张三"
p.age=19
p.sex='男'
// 对象 转 json
val jsonStr=JSON.toJSON(p).toString
println(jsonStr) // {"sex":"男","name":"张三","id":1001,"age":19}
}
若没有 get/set 方法,将无法解析
json 转对象
class Person{
/**
* id
*/
var id:Int=_
/**
* 姓名
*/
var name:String=_
/**
* 年龄
*/
var age:Int=_
/**
* 性别
*/
var sex:Char=_
}
def main(args: Array[String]): Unit = {
// 对象 转 json
// classOf[Person] 用于获取 Person 的 class
val p:Person=JSON.parseObject("""{"sex":"男","name":"张三","id":1001,"age":19}""",classOf[Person])
val info=s"id=${p.id},姓名=${p.name},年纪=${p.age},性别=${p.sex}"
println(info) // id=0,姓名=null,年纪=0,性别=
}
重新指定 @BeanProperty
class Person{
/**
* id
*/
@BeanProperty var id:Int=_
/**
* 姓名
*/
@BeanProperty var name:String=_
/**
* 年龄
*/
@BeanProperty var age:Int=_
/**
* 性别
*/
@BeanProperty var sex:Char=_
}
def main(args: Array[String]): Unit = {
// 对象 转 json
// classOf[Person] 用于获取 Person 的 class
val p:Person=JSON.parseObject("""{"sex":"男","name":"张三","id":1001,"age":19}""",classOf[Person])
val info=s"id=${p.id},姓名=${p.name},年纪=${p.age},性别=${p.sex}"
println(info) // id=1001,姓名=张三,年纪=19,性别=男
}
关于 @BeanProperty
注解,我也不太明白为什么开发者会这么设计,对于我来说就是不合理。但他的作用就是用于兼容JavaApi。
继承
java中的继承
语法:
[修饰符] class 类名 extends 父类名{类体}
特性:
- 使用
extends
关键字用于继承 - 被标识为
final
的类不能被继承, - 只能单继承
- 被继承的类可以获取它所有非
private
修饰的属性和方法。 - 子类可以重写父类的方法
scala中的继承
语法:
class 类名[(参数列表)] extends 父类名[参数列表]
特性:
- 使用
extends
关键字用于继承 - 同java一致,scala也只能用于单继承。
-
extends
并不是继承一个类的标志,也可用于 特质类上。 - 被
private
修饰的不能被继承。
定义
class Person{
var name="your name?"
def add(x:Int,y:Int)=x+y
}
class Student extends Person
调用
def main(args: Array[String]): Unit = {
val stu=new Student
println(stu.name) // your name?
println(stu.add(1,2)) // 3
}
方法重写:
在 Person
中有个add
方法,可能该方法并不是我们想要,我需要的功能是 x-y
此时就需要对该方法进行重写。
在scala 中重写父类中的方法,需要使用 override
关键字修饰。
class Student extends Person {
// 重写 父类的 add 方法
override def add(x: Int, y: Int): Int = x-y
}
调用:
def main(args: Array[String]): Unit = {
val stu=new Student
println(stu.name) // your name?
println(stu.add(1,2)) // -1
}
属性重写
在scala中除了可以对方法进行重写外,还可以对属性进行重写
class Student extends Person {
name="王富贵"
override def add(x: Int, y: Int): Int = x-y
}
def main(args: Array[String]): Unit = {
val stu=new Student
println(stu.name) // 王富贵
println(stu.add(1,2)) // -1
}
注意:以上这种不能称为重写,而只是在子类定义了一个与父类相同的属性,覆盖了父类中的属性,可以称为改了父类中该属性的值。
那么什么才叫属性重写呢?
- 父类中的属性是必须定义成
val
,不能使用private
修饰。 - 重写属性和重写方法一样,都会用到
override
关键字。
重新在Person
中定义一个属性hobby
class Person{
var name="your name?"
val hobby="唱跳,rap"
def add(x:Int,y:Int)=x+y
}
在Student
中重新父类的 hobby
属性,比如打篮球
class Student extends Person {
name="坤坤"
override val hobby: String = "打篮球"
override def add(x: Int, y: Int): Int = x-y
}
运行
def main(args: Array[String]): Unit = {
val stu=new Student
println(stu.name) // 坤坤
println(stu.hobby) // 打篮球
println(stu.add(1,2)) // -1
}
温馨提示
:没有冒犯任何姓名中带坤
的人,不过我觉得,能看到此文章的人应该都是程序员吧?没有哪个程序员会是某垃圾
的粉丝吧。
调用父类中的方法
在java中若要调用父类中的 方法,会使用supper
关键字,在scala中也是一样。
class Student extends Person {
name="坤坤"
override val hobby: String = "打篮球"
override def add(x: Int, y: Int): Int = {
// 调用父类中的 add 方法
println(s"supper add=:${super.add(x,y)}")
x-y
}
}
构造器
继承中父子类的构造器知识点也是很重要的。
- 若父类中声明了主构造器,子类必须向父类的主构造器中传参
// 父类
class Person(val id:Int,val name:String){
}
// 子类
class Student(override val id:Int, override val name:String, age:Int) extends Person(id,name){
}
- 若父类中主构造器无参,副构造器有参数,继承父类时,可以不用对父类构造器赋值;表示默认使用父类的无参构造器。
class Person{
def this(id:Int,name:String){
this()
}
}
class Student(val id:Int, val name:String, age:Int) extends Person{
}
也可以指定父类的副构造器;也是可以的。
class Person{
def this(id:Int,name:String){
this()
}
}
class Student(val id:Int, val name:String, age:Int) extends Person(id,name){
}
- 调用父类构造器
在java中,使用supper
关键字,并且必须指定在子类的第一行。
如:
class A{}
class B extends A{
// 调用父类构造器
public B(){
super();
}
}
在scala中也是如此。此时有个,有个问题,在scala中定义副构造器必须调用主构造器或其他副构造器。正常的情况下,this()
会放在构造器的第一行,此时需要调用父类的构造器,是super()
放在第一行还是this()
放在第一行呢?
答案:都不行,scala不支持这么做。
多态
多态:父类的引用指向子类的实例。
def main(args: Array[String]): Unit = {
val person:Person=new Student()
}
思考一:person.add(1,2) 调用是父类的还是子类的?
def main(args: Array[String]): Unit = {
val person:Person=new Student()
println(person.add(1,2))
}
不用想,肯定是子类的,上面的案例中,
Student
重写了Person
的add
方法。所以答案是-1
;这个应该知道。
思考二:person.name 是 "坤坤" 还是 "your name?"?
def main(args: Array[String]): Unit = {
val person:Person=new Student()
println(person.name)
}
答案是 "坤坤" 在scala 中 属性也具有多态性,这点和java一定要区分开。
回想java 中,对于方法,运行看右(new 的部分),对于属性,运行看左(引用部分)。
public class Demo101 {
public static void main(String[] args) {
A a=new B();
System.out.println(a.name);
a.say();
}
}
class A{
String name="aaaa";
public void say(){
System.out.println("a...");
}
}
class B extends A{
String name="bbbb";
public void say(){
System.out.println("b...");
}
}
虽然A和B都有 name属性,但是执行中结果仍然是父类A的name,而方法不一样,需要看new出来最终的对象是什么。
结果:
aaaa
b...
这里涉及到了java 中静态分派与动态分派相关的概念
思考三: person.hobby 又是什么?
答案:肯定是 Student 中的 "打篮球";这个我就写,可以下去动手试试。
总结:
这就是 scala
中的 三大特性继承
、封装
、多态
;
基本上和java类似,为了区分开的是 java中 属性不具备多态性,scala中属性具备多态性。