写点什么

Scala 基础 (六):面向对象(下篇)

  • 2022 年 7 月 07 日
  • 本文字数:3945 字

    阅读完需:约 13 分钟

在之前的文章中总结了 Scala 面向对象模块基础的内容,接下来学习面向对象的高阶内容。

一、抽象类

如何定义?


// 定义抽象类abstract class 类名{  // 定义抽象属性 不赋初始值  val|var 变量名: 类型  // 定义抽象方法  只声明,不实现  def 方法名(): 返回值类型}
复制代码


  • 如果父类为抽象类,那么子类需要将抽象的属性和方法实现,否则子类也需声明为抽象类

  • 重写非抽象方法需要用 override 修饰,重写抽象方法则可以不加 override

  • 子类中调用父类的方法使用 super 关键字

  • 子类对抽象属性进行实现,父类抽象属性可以用 var 修饰;

  • 子类对非抽象属性重写,父类非抽象属性只支持 val 类型,而不支持 var。因为var修饰为可变量,子类继承后可以直接使用修改,没有必要重写。


举个栗子:


// 定义抽象类abstract class Person{  // 定义非抽象属性  val name: String = "person"  // 抽象属性  var age: Int
// 非抽象方法 def eat(): Unit ={ println("Person eat") }
// 抽象方法 def sleep(): Unit}
class Student extends Person{ // 实现抽象属性和方法 var age: Int = 18
def sleep(): Unit ={ println("student sleep") }
// 重写非抽象属性和方法 override val name: String = "Student"
override def eat(): Unit = { super.eat() println("Student eat") }}
复制代码


匿名子类


和 Java 一样,可以通过包含带有定义或重写的代码块的方式创建一个匿名的子类。匿名子类只针对抽象类和接口。


举个栗子:


object Test10_AnnoymousClass {  def main(args: Array[String]): Unit = {    // 匿名子类实现    val person = new Person {      override var name: String = "alice"
override def eat(): Unit = println("person eat") }
println(person.name) person10.eat() }}
abstract class Person { var name: String def eat():Unit}
复制代码

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

Scala 是一门完全面向对象的语言,没有静态操作。为了能够和 Java 语言进行交互,使用单例对象来取代static关键字的语义,伴生类的静态声明都可放在伴生对象中。


基本语法:


object 伴生对象名{  val country: String="China"}
复制代码


  • 编译后会生成两个类,伴生对象是伴生类(类名为对应类后加 $符号)的单例对象。

  • 单例对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致,必须在同一个文件中。

  • 单例对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问。

  • Scala 中 obj(arg)的语句实际是在调用该对象的 apply 方法,即 obj.apply(arg)。用以统一面向对象编程和函数式编程的风格。

  • Scala 底层对于伴生对象的apply方法进行了优化,调用时可以省略方法名,直接使用单例对象的名称+调用apply方法的参数

  • 当使用 new 关键字构建对象时,调用的其实是类的构造方法,当直接使用类名构建对象时,调用的其实时伴生对象的 apply 方法


举个栗子:


// 伴生类// 构造器私有化class Student private(val name: String, val age: Int) {  def printInfo(): Unit = {    println(s"Student11: $name $age ${Student11.school}")  }}
// 伴生对象object Student { // 看作静态的属性 val school: String = "清华" // 定义类对象实例创建的方法 def newStudent(name: String,age: Int): Student11 = new Student11(name,age)
def apply(name: String, age: Int): Student11 = new Student11(name, age)}

object Test11_Object { def main(args: Array[String]): Unit = { val student = Student.newStudent("alice", 12) student.printInfo()
val student1 = Student.apply("haha", 12)
val student2 = Student("lolo", 34) student2.printInfo() }}
复制代码

三、特质

如何定义特质?


trait 特质名 {  // 代码块}
复制代码


  • Scala 语言中,采用特质 trait(特征)来代替接口的概念

  • 多个类具有相同的特征时,就可以将这个特征提取出来,用继承的方式来复用

  • Scala 中的 trait 中即可以有抽象属性和方法,也可以有具体的属性和方法,一个类可以混入(mixin)多个特质。这种感觉类似于 Java 中的抽象类。


基本语法:


有父类 class extends baseClass with trait1 with trait2 ... {}没有父类 class extends trait1 with trait2 ... {}
复制代码


  • 如果一个类在同时继承特质和父类时,应当把父类写在 extends 后。

  • 特质中可以定义抽象和非抽象的属性和方法。


特质叠加引发两种冲突


第一种:一个类(Sub)混入的两个 trait(TraitATraitB)中具有相同的具体方法,且两个 trait 之间没有任何关系



如果当前父类和特质里面出现了相同的属性和方法,就会起冲突,必须在当前实现的子类(sub)中进行重写。


举个栗子:


class Person {    val name: String = "Person"    var age: Int = 18
def sayHi(): Unit = { println(s"hello from : $name") }}
trait Young { // abstract and non-abstract attribute var age: Int val name: String = "young"
// method def play(): Unit = { println(s"young people $name is playing") } def dating(): Unit}
trait Knowledge { var amount: Int = 0 def increase(): Unit = { amount += 1 }}
trait Talent { def increase(): Unit = { println("increase talent") }}
class Student extends Person with Young with Knowledge with Talent{ override val name: String = "alice"
def dating(): Unit = { println(s"Sutdent $name $age is dating") }
def study(): Unit = println(s"Student $name is studying")
override def sayHi(): Unit = { super.sayHi() println(s"hello from : student $name") }
override def increase(): Unit = { super.increase() println(s"studnet $name knowledge increase: $amount") }}
object Trait { def main(args: Array[String]): Unit = { val s = new Student() s.sayHi() s.increase()
s.study() s.increase()
s.play() s.increase()
s.dating() s.increase() }}
复制代码


第二种:一个类(Sub)混入的两个 trait(TraitATraitB)中具有相同的具体方法,且两个 trait 继承自相同的 trait(TraitC



Scala 采用了特质叠加的策略来解决这类冲突。


举个栗子:


trait Ball {  def describe(): String = "ball"}
trait ColorBall extends Ball { val colorBall: String = "red"
override def describe(): String = { colorBall+"-----"+super.describe() }}
trait CategoryBall extends Ball{ val categoryBall: String = "foot"
override def describe(): String = categoryBall+"-----"+super.describe()}
// 叠加顺序 MyFootBall -> ColorBall -> CategoryBall -> Ballclass MyFootBall extends CategoryBall with ColorBall { override def describe(): String = "my ball is a" + super.describe()}
复制代码


  • 当一个类混入多个特质的时候,scala 会对所有的特质及其父特质按照一定的顺序进行排序,而此案例中的super.describe()调用的实际上是排好序后的下一个特质中的 describe()方法。

  • 要调用某个指定的混入特质中的方法,可以增加约束:super[]


  super[CategoryBall].describe()
复制代码


自身类型


  • 自身类型可实现依赖注入的功能。

  • 一个类或者特征指定了自身类型的话,它的对象和子类对象就会拥有这个自身类型中的所有属性和方法。

  • 是将一个类或者特征插入到另一个类或者特征中,属性和方法都就像直接复制插入过来一样,能直接使用。但不是继承,不能用多态。


举个栗子:


object Test16_TraitSelfType {  def main(args: Array[String]): Unit = {    val user = new RegisterUser("alice", "1212121")    user.insert()  }}
// 用户类class User(val name: String,val password: String){
}
trait UserDao{ // 定义自身类型 _: User => // 注册用户 def insert():Unit={ println (s" insert into db: = ${this.name} , ${this.password}") }}
// 定义注册用户class RegisterUser(name: String,password: String) extends User(name,password) with UserDao
复制代码


  • _: SelfType =>,其中_的位置是别名定义,也可以是其他,_指代 this。插入后就可以用this.xxx来访问自身类型中的属性和方法了。


抽象类和特质的区别?


优先使用特质。一个类可以扩展多个特质,但是只能扩展一个抽象类。需要构造函数参数,使用抽象类,抽象类可以定义带参的构造器,特质只是无参的构造器。

四、扩展内容

类型检查和转换


  • obj.isInstanceOf[T]:判断 obj 是不是 T 类型。

  • obj.asInstanceOf[T]:将 obj 强转成 T 类型

  • classOf 获取对象的类名。结果是class package.xxx.className

  • 获取对象的类:obj.getClass


枚举类


  • 需要继承 Enumeration

  • Value类型定义枚举值。


// 定义枚举类object WorkDay extends Enumeration{  val MONDAY = Value(1,"星期一")  val TUESDAY = Value(2,"星期二")}
println(WorkDay.MONDAY)
复制代码


应用类


  • 继承 App,包装了 main 方法,就不需要显式定义 main 方法了,可以直接执行。


object TestApp extends App{  println("app start"}
复制代码


定义新类型


  • 使用 type 关键字可以定义新的数据数据类型名称,本质上就是类型的一个别名

  • type SelfDefineType = TargetType


 type MyString= String  val a: MyString = "abc"  println(a)
复制代码


本次分享的内容到这里就结束了,希望对大家学习 Scala 语言有所帮助!!!


发布于: 刚刚阅读数: 3
用户头像

该来的总会来,或迟或早。🎈 2022.06.13 加入

有JAVA方面3年学习经验,csdn/51cto等平台优质作者,华为云云享专家、阿里云受邀专家博主,擅长JAVA,大数据等方面的技术等。

评论

发布
暂无评论
Scala 基础 (六):面向对象(下篇)_scala_百思不得小赵_InfoQ写作社区