Scala 面向对象【中】

简介: Scala 面向对象【中】

Scala 面向对象(中)

1、继承和多态

       用法和Java基本一致。

基本语法

       class 子类名 extends 父类名称 {}

案例演示

object Test07_Inherit {
  def main(args: Array[String]): Unit = {
    val student = new Student1("GG Bond",18)
    student.printInfo()
  }
}
//定义一个父类
class Person1(){
  var name: String = _
  var age: Int = _
  println("1. 父类主构造器调用")
  def this(name: String,age: Int){
    this()
    println("2.父类的辅助构造器被调用")
    this.name = name
    this.age = age
  }
  def printInfo(): Unit = {
    println(s"${name} ${age}")
  }
}
//继承父类可以指定调用父类的构造器(需要给继承的父类传入参数,使用父类的主构造器还是辅助构造器取决于传入的参数是什么) 也可以不调用(直接extends Person1)
class Student1(name: String,age: Int) extends Person1(name,age){
  var school: String = _
  println("3. 子类的主构造器被调用")
  def this(name: String,age: Int,school: String){
    this(name,age)  //调用主构造方法
    this.school = school
    println("4. 子类的辅助构造器被调用")
  }
  //重写父类的方法
  override def printInfo(): Unit = {
    println(s"${name} ${age} ${school}")
  }
}

2、多态

2.1、动态绑定

Java中,只有方法的动态绑定的,但在Scala中,方法和属性都是动态绑定的。

       在面向对象编程中,使用父类的引用来引用子类的对象,从而实现对不同子类对象的统一操作。虽然使用父类引用创建子类对象可能无法直接调用子类特有的方法,但多态的真正价值在于增强程序的灵活性和可扩展性。


       通过多态,您可以将一组具有相似行为的子类对象看作是它们共同的父类类型,这样可以更方便地管理和处理这些对象。例如,假设有一个动物类作为父类,有狗类和猫类作为子类,它们都有发出声音的行为。如果我们使用父类的引用来引用狗类和猫类的对象,我们可以通过调用父类的方法来让这些不同的子类对象发出声音,而无需关心具体是哪种类型的子类对象。


       此外,多态也可以让代码更易于扩展和维护。当我们需要新增一个新的子类时,只需继承自父类并实现相应的方法,而不需要修改已有的代码。这样可以避免影响已有的代码和程序的稳定性。

多态案例

object Test {
  def main(args: Array[String]): Unit = {
    //多态
    val dog:Animal = new Dog
    val cat:Animal = new Cat
    dog.say() //汪汪汪~
    cat.say() //喵喵喵~
  }
}
abstract class Animal{
  //定义一个抽象方法
  def say():Unit
}
class Dog extends Animal {
  def say(): Unit = println("汪汪汪~")
}
class Cat extends Animal{
  def say(): Unit = println("喵喵喵~")
}
Java:
public class Test {
    public static void main(String[] args) {
        Worker worker = new Worker();
        System.out.println(worker.name);
        worker.hello();
        System.out.println("============");
        //多态
        Person person = new Worker();
        System.out.println(person.name);
        person.hello();
    }
}
class Person{
    String name = "person";
    public void hello(){
        System.out.println("hello person");
    }
}
class Worker extends Person{
    String name = "worker";
    public void hello(){
        System.out.println("hello worker");
    }
}

输出结果

worker
hello worker
============
person
hello worker
Scala:

Scala在重写父类的属性和方法时,需要再属性名和方法名之前使用 “override” 关键字。

package chapter05
object Test08_DynamicBind {
  def main(args: Array[String]): Unit = {
    val student:Student8 = new Student8
    println(student.name)
    student.hello()
    println("=============")
    //多态
    val person:Person8 = new Student8
    println(person.name)
    person.hello()
  }
}
class Person8{
  val name = "person"
  def hello(): Unit = {
    println("hello person")
  }
}
class Student8 extends Person8 {
  override val name = "student"
  override def hello(): Unit = {
    println("hello student")
  }
}

运行结果

student
hello student
=============
student
hello student

可以看到,Scala通过父类创建子类对象后,在调用对象的方法时,会动态绑定当前的对象的属性和方法。

3、抽象类

3.1、抽象属性和方法

Scala中定义变量和方法时一般必须要求赋初始值,但是在抽象类中可以不遵守。

  • 在Scala中,只要一个类中有抽象方法和抽象属性,该类必须是抽象类。
  • 在Scala中,一个抽象类的属性和方法可以是已实现的。
  • 定义抽象类abstract class Person{} //通过 abstract 关键字标记抽象类
  • 定义抽象属性val|var name:String //一个属性没有初始化,就是抽象属性
  • 定义抽象方法def hello():String //只声明而没有实现的方法,就是抽象方法

3.2、抽象类的继承与重写

  1. 如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类
  2. 重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override
  3. 子类中调用父类的方法使用 super 关键字
  4. 子类对抽象属性进行实现,父类抽象属性可以用 var/val 修饰;
  5. 子类可以直接对一个被 var 修饰的变量进行赋值来实现重写,不需要使用 override 关键字,所以对于非抽象属性,不建议用 var 修饰。
abstract class Person9{
  //抽象属性
  val name: String
  //非抽象属性
  val age: Int = 18 //被val 修饰 子类需要重写
  var school = "山西农业大学" //被var修饰 子类不需要重写
  //抽象方法
  def hello():Unit
  //非抽象方法
  def eat(): Unit = {
    println("person eat")
  }
}
class Student9 extends Person9{
  //实现抽象属性
  val name: String = "李元芳"
  //实现抽象方法
  def hello(): Unit = {
    println("hello")
  }
  //重写非抽象属性
  override val age = 10
  school = "北京中医药大学"  //父类中被 var 修饰的非抽象变量可以直接重新赋值 不需要重写 所以不建议非抽象属性用 var 修饰
  //重写非抽象方法
  override def eat(): Unit = {
    super.eat()
    println("student eat")
  }
}

4、匿名子类

匿名子类,也就是说不需要继承抽象父类,直接在定义对象的时候重写抽象属性和方法。

object Test {
  def main(args: Array[String]): Unit = {
    val person:Person10 = new Person10 {
      override var name: String = "GG Bond"
      override def hello(): Unit = {
        println("hello gays")
      }
      println(person.name)
      person.hello()
    }
  }
}
//定义抽象类
abstract class Person10{
  //定义抽象属性和方法
  var name: String
  def hello(): Unit
}

5、单例对象(伴生对象)

       Scala语言是 完全面向对象 的语言,所以并没有静态的操作(即在 Scala 中没有静态的概念)。但是为了能够和Java 语言交互(因为 Java 中有静态概念),就产生了一种特殊的对象来 模拟类对象, 该对象为 单例对象 。若单例对象名与类名一致,则称该单例对象这个类的生对象,这个类的所有“静态”内容都可以放置在它的伴生对象中声明。

说明

  • 单例对象采用 object 关键字声明
  • 单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
  • 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。
  • 伴生类和伴生对象必须名字相同且在一个文件中
object Test {
  def main(args: Array[String]): Unit = {
    val bob = new Student11("bob", 18)
    bob.printInfo()
  }
}
//定义伴生类
class Student11(val name: String,val age: Int){
  def printInfo(): Unit = {
    println(s"${name} ${age} ${Student11.school}")
  }
}
//定义伴生对象
object Student11{
  val school: String = "北京中医药大学"
}

5.1、主构造器私有化

       有些时候我们不希望直接通过主构造器来创建对象实例,而是通过工厂方法来创建对象,在Scala中,我们可以借助伴生对象和伴生类可互相调用的特点来实现:

我们需要在伴生类的主构造器参数前使用 private 关键字进行修饰

object Test {
  def main(args: Array[String]): Unit = {
//    val bob = new Student11("bob", 18)
//    bob.printInfo()
    val bob = Student11.newStudent("bob", 18)
    bob.printInfo()
  }
}
//定义伴生类
class Student11 private (val name: String,val age: Int){
  def printInfo(): Unit = {
    println(s"${name} ${age} ${Student11.school}")
  }
}
//定义伴生对象
object Student11{
  val school: String = "北京中医药大学"
  //定义一个类的对象实例的创建方法(工厂方法)
  def newStudent(name: String,age: Int): Student11 = {
    new Student11(name,age)
  }
}

5.2、apply方法

使用 apply方法来实现工厂模式,可直接使用类名来创建对象,不需要显式地使用apply方法。

object Test {
  def main(args: Array[String]): Unit = {
    val alice = Student11.apply("alice",10)
    val tom = Student11("alice",10) //scala 调用apply方法可以省去方法名 简化代码
    alice.printInfo()
    tom.printInfo()
  }
}
//定义伴生类
class Student11 private (val name: String,val age: Int){
  def printInfo(): Unit = {
    println(s"${name} ${age} ${Student11.school}")
  }
}
//定义伴生对象
object Student11{
  val school: String = "北京中医药大学"
  def apply(name: String,age: Int): Student11 = {
    new Student11(name,age)
  }
}

5.3、单例设计模式

       单例设计模式,始终只有一个对象,即使我们在创建对象的时候,赋予不同的属性,但是返回的结果都只会是第一个对象的实例。

object Test12_Singleton {
  def main(args: Array[String]): Unit = {
    val student2 = Student12.getInstance("bob",20)
    student2.printInfo()    //bob 20 北京中医药大学
    val student1 = Student12.getInstance("alice",18)
    student1.printInfo()    //bob 20 北京中医药大学
    //输出相同的对象引用
    println(student1)    
    println(student2)
  }
}
//定义伴生类
class Student12 private (val name: String,val age: Int){
  def printInfo(): Unit = {
    println(s"${name} ${age} ${Student12.school}")
  }
}
//饿汉式
//object Student12{
//  val school = "北京中医药大学"
//
//  private val student: Student12 = new Student12("alice",18)
//
//  def getInstance(): Student12 = student
//}
//懒汉式
object Student12{
  val school = "北京中医药大学"
  private var student:  Student12 = _
  def getInstance(name: String,age: Int): Student12 = {
    if (student == null){
      //没有对象实例的话就创建一个
      student = new Student12(name,age)
    }
    student
  }
}

使用伴生对象可以很轻松地实现各种设计模式。

相关文章
|
4月前
|
Java Scala Python
Scala面向对象【上】
Scala面向对象【上】
|
4月前
|
分布式计算 Java Scala
Scala:面向对象、Object、抽象类、内部类、特质Trait(二)
Scala:面向对象、Object、抽象类、内部类、特质Trait(二)
63 0
|
4月前
|
大数据 Scala
Scala面向对象练习题34道
Scala面向对象练习题34道
58 0
|
4月前
|
Java Scala
Scala面向对象【下】
Scala面向对象【下】
|
7月前
|
Java Scala
Scala面向对象4
Scala面向对象4
27 0
|
7月前
|
Scala
Scala面向对象3
Scala面向对象3
24 1
|
7月前
|
分布式计算 Java Scala
Scala面向对象2
Scala面向对象2
36 0
|
7月前
|
Java 编译器 Scala
Scala面向对象1
Scala面向对象1
36 0
|
SQL JSON 前端开发
Scala的面向对象与函数编程
Scala的面向对象与函数编程
Scala的面向对象与函数编程
|
存储 算法 Java
Scala学习三-面向对象
前面我们已经学习了特质类似接口,其可以被继承,同时如果需要继承多个特质的话,则需要使用extends…with…进行继承。其类似java中的接口和抽象方法的结合体,但又比java中的其要强大,因为其可以定义抽象字段和普通字段、抽象方法和普通方法。而在java中接口中可以定义常量,不能定义变量。同时特质还可以继承class类,而在java中接口通常是用来实现的。 Object继承trait
116 0
Scala学习三-面向对象