Scala(二)-- 类、对象、继承、特质

本文详细介绍了Scala中的类、特质、抽象类以及对象的使用,包括单例对象、伴生对象、应用程序对象的概念和特点。同时讨论了unapply和apply方法在模式匹配和工厂方法中的作用,以及Scala中的继承机制,包括覆盖父类方法和使用super关键字。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.类

1)在scala中,类并不用声明为public

如果没有定义构造器,类会有一个默认的无参构造器
var修饰的变量,对外提供getter setter方法
val修饰的变脸,提供getter方法,没有setter方法
var name:String = _
_表示一个占位符,编译器会根据你变量的具体类型赋予相应的初始值
使用占位符,变量类型必须指定,val修饰的变量不能使用占位符

class StuDemo {
  //_是占位符,使用时需要属性的类型信息
  var name:String = _
  //val使用前必须初始化,修饰的变量不能使用占位符
  //除了接口或者抽象类,里面的属性都需要初始化
  val age:Int = 10

  //类私有字段,当前类的其它方法或者类的伴生对象可以访问
  private var hobby:String = "traveing"
  //生成get set方法,但方法是私有的,本类的方法可以访问

  //对象私有字段--只有在当前对象方法可以调用
  private [this] val cardinfo = "123456"
  //没有get set方法

  def compare(obj:StuDemo)={
    if(this.age != obj.age) this.age-obj.age
    //else if(this.cardinfo != obj.cardinfo)
  }
}

object TestStu{
  def main(args: Array[String]): Unit = {
    //如果不定义构造方法,有一个无参的构造方法
    val obj = new StuDemo
    println(obj.age)
    println(obj.name)
  }
}

2)手动创建变量的getter和setter方法需要遵循以下原则:

(1) 字段属性名以“_”作为前缀,如:_x
(2) get方法定义为:def x = x
(3) set方法定义时,方法名为属性名去掉前缀,并加上后缀,后缀是:“def x
=”

//字段属性名以“_”开始
class Point {
  private var _x = 0
  private var _y = 0
  private val bound = 100

  //get方法,方法名是属性名去掉“_”
  def x = _x
  //set方法,方法名是属性名去掉“_”,在后面加一个“_”
  def x_=(newValue:Int) = {
    if (newValue<bound)_x = newValue else println("nothing")
  }

  def y = _y
  def y_= (newValue:Int)={
    if (newValue < bound)_y =newValue else println("out of bound")
  }
}

object testPoint{
  def main(args: Array[String]): Unit = {
    val pointObj = new Point()
    println(pointObj.x)
    pointObj.x_=(200)
    pointObj.y_=(150)
  }
}

3)Scala中定义类的构造方法

scala中构造分为主构造器和辅助构造器
(1)主构造的参数直接放置于类名之后
(2)主构造器会执行类定义中的所有语句
(3)通过private设置的主构造器的私有属性
(4)辅助构造器名称为this,(def this(…))通过不同参数进行区分,第一句一定是调用主构造方法或者其他的辅助构造方法

//主构造方法的参数直接放在类名之后
class ClassConstructor private (var name:String,val price:Double) {
//私有时直接 ClassConstructor private...私有构造方法不能直接调用构造对象

  println(name + "," + price)  //会执行类中的所有语句

  private  var  weight:Double = 0
  private  var  color:String = _

  def myPrintln = println(name + "," + price)

  //辅助构造方法,this,第一句一定是调用主构造方法或者其他的辅助构造方法
  def this(name:String,price:Double,weight:Double){
    this(name,price)
    this.weight = weight
  }

  def this(name:String,price:Double,weight:Double,color:String){
    this(name,price,weight)
    this.color = color
  }
}

object testConstructor{
  def main(args: Array[String]): Unit = {
    val obj = new ClassConstructor("apple",20.0,100.0)
    //obj.myPrintln
  }
}

2.特质

特质的定义除了使用关键字trait之外,与类定义无异

接口中可以:
声明一个有值的字段、声明一个抽象字段、声明一个=带有具体实现的方法、声明一个抽象方法
在triat中可以定义抽象方法,就与抽象类中的抽象方法一样,只要不给出方法的具体实现即可
类可以使用extends关键字继承trait,注意,这里不是implement,而是extends,在scala中没有implement的概念,无论继承类还是trait,统一都是extends

//特质(接口)
trait Flyable{
  //接口中可以定义

  //声明一个有值的字段

  //接口中的字段:类中继承了这个字段,接口中的字段会直接添加到实现了(继承了)该接口的类中
  //普通类中的字段:如果是普通的类,父类中的字段在子类中会有一个引用地址,指向父类的属性

  val distant:Int = 100

  //声明一个抽象字段
  val height:Int

  //声明一个=带有具体实现的方法
  def fly:String  = {
    "I can fly"
  }

  //声明一个抽象方法
  def fight:String
}

trait Logger{
  def log(msg:String)
}

class ConsoleLogger extends Logger{
  //类中实现抽象方法,重写抽象方法可以不用override
  def log(msg: String): Unit = println(msg)
}

class Account{
  var balance = 0.0
}

//一个以上的特质用with
class SavingAccount extends Account with Logger{
  //override def log(msg: String): Unit = println("SavingAccount")
  override def log(msg: String): Unit = println(msg)
  def withdraw(amount:Double):Unit = {
    if(amount > balance) log("余额不足")
    else balance -= amount
  }
}

//单继承,可以实现多接口
object TraitDemo {
  def main(args: Array[String]): Unit = {
//    val obj = new ConsoleLogger
//    obj.log("trait test")

    val obj = new SavingAccount
    obj.withdraw(100)
  }
}

类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字
scala不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可

特质继承类(特别点)
在Scala中,trait也可以继承自class,此时这个class就会成为所有继承该trait的类的父类

//特质继承类
class MyUtil{
  def printmsg(msg:String) = println(msg)
}

trait  MyTrait extends MyUtil{
  def log (msg:String) = printmsg(msg)
}

class Demos(val name:String) extends MyTrait{
  def sayHello:Unit = {
    log(name+"0000000")
    printmsg(name+"111111")
  }
}

object TraitDemo2 {

  def main(args: Array[String]): Unit = {
    val obj = new Demos("zhangfasd")
    obj.sayHello
  }
}

3.抽象类

定义一个抽象类:abstract
如果某个类至少存在一个抽象方法或一个抽象字段,则该类必须声明为abstract。

abstract class Person{
  //抽象字段,有初值的字段,抽象方法:没有方法体,是抽象方法,具体实现的方法、
  val age = 20
  var name:String
  def id:Int  //没有初始值,抽象字段
}

class Employ extends Person{
  //实现抽象字段或者抽象方法
  var name:String = "tom"
  def id = name.hashCode
}


//object
object AbstactclassDemo {
  def main(args: Array[String]): Unit = {
    val obj = new Employ
    println(obj.id)
  }
}

4、对象

1)单例对象

在Scala中没有静态方法和静态字段,但是可以使用object这个语法结构来达到同样的目的

object ObjectTest {
  def main(args: Array[String]): Unit = {
    val s = SessionFactory
    println(s.getSession())
    println(s.getSession().size)
    println(s.removeSession)
    println(s.getSession().size)
  }
}

class Session{

}

object SessionFactory{
  print("SessionFactory被执行")
  //计数器
  var i = 5
  //存放seesion对象
  val sessions = new ArrayBuffer[Session]()
  //session添加新的session
  while(i>0){
    sessions.append(new Session)
    i -= 1
  }

  //获取session对象
  def getSession() = sessions

  //删除session对象
  def removeSession:Unit = {
    val session = sessions(0)
    sessions.remove(0)
    println("被删除的session对象"+session)
  }
}

类和单例对象的区别是:

单例对象不能带参数,单例对象不能用new关键字实例化,所以没有机会传递给它实例化的参数。
单例对象在第一次访问的时候才会初始化。
当单例对象与某个类同名时,它被称为类的伴生对象,类和伴生对象必须定义在一个源文件里,类称为该单例对象的伴生类,类和他的伴生对象可以互相访问其私有成员。
不与伴生类共享名称的单例对象被称为独立对象,可以作为相关功能的工具类,或者scala应用程序的入口点。

2)伴生对象

在Scala的类中,与类名相同并且用object修饰的对象叫做伴生对象,类和伴生对象之间可以相互访问私有的方法和属性,他们必须存在同一个源文件中

//伴生对象要求:名字和类名是一致的,在一个源文件里
//优势:类和伴生对象之间可以互相访问彼此的私有属性或者方法
class AccountInfo{
  //这里可以定义私有方法
  var  id = AccountInfo.newUniqueNumber()
  private var balance = 0
}

object  AccountInfo{
  private var lastNumber = 0
  private  def newUniqueNumber()={
//    val obj =new AccountInfo
//    println(obj.balance)

    lastNumber += 1//赋值语句的返回值是() Unit
    println("lastNumber:"+lastNumber)
    lastNumber//返回值
  }
}

object ObjectTest2 {
  def main(args: Array[String]): Unit = {
    val obj = new AccountInfo
    obj.id
  }
}

对伴生对象的理解(单例对象)
伴生对象要求:名字和类名是一致的,在一个源文件里
优势:类和伴生对象之间可以互相访问彼此的私有属性或者方法,private this 修饰除外

3)应用程序对象

Scala程序都必须从一个对象的main方法开始,可以通过扩展App特质,不写main方法。

object Hello extends App{
println("Hello World")
}

5.unapply 和apply方法

unapply 和apply方法的区别,以及使用场景:

apply unapply 都在伴生方法中被定义,都是系统会隐式调用的
apply方法(注入):创建类的对象,不使用new关键字时被系统隐式调用,是创建对象 的工厂
unapply方法(提取):提取对象 的属性信息时,一般被用于模式匹配或者偏函数中

apply:

object AccountInfo1{
  private var lastNunber = 0
  //apply方法
  def apply(a:Int)={  //被系统隐式调用,使用类名,没有new,会自动使用apply方法:var arr = Array(1,2,3,4,5)
    println("apply 方法被调用")
    lastNunber = a+1
    lastNunber
  }
}

object ObjectTest3 {
  def main(args: Array[String]): Unit = {
    println(AccountInfo1(1))
    println(AccountInfo1.apply(2))
  }
}

unapply:

object  CustomID{
  def apply(name:String) =  s"$name---${Random.nextLong()}" // 差值器

//some()  none
  def unapply(CustomID:String): Option[String] = {
    println("unapply方法被调用")
    val name = CustomID.split("---").head
    if(name.nonEmpty) Some(name) else None
  }
}


object ObjectTest4 {

  def main(args: Array[String]): Unit = {
    val custoobj = CustomID("jerry")
    custoobj match {
      case CustomID(name) => println(name)
        //相当于java中的default
      case _ => println("not match")
    }
  }
}

6.继承

Scala中,让子类继承父类,与Java一样,也是使用extends关键字
继承就代表,子类可以从父类继承父类的属性和方法;然后子类可以在自己内部放入父类所没有,子类特有的属性和方法;使用继承可以有效复用代码
子类可以覆盖父类的属性和方法;但是如果父类用final修饰,则该类是无法被继承的,属性和方法用final修饰,属性和方法是无法被覆盖的

class Person1{
  private var name = "tom"
  val age = 0
  def getName = name
}

class Student extends Person1{
  private var score = 100
  def getScore = score
  //super调用父类的同名方法
  //override重写父类的非抽象方法,非抽象字段
  override val age = 0
  override def getName: String = super.getName + "  " +getScore
}

object ExtendsTest1 {
  def main(args: Array[String]): Unit = {
    val s = new Student
    println(s.getScore)
    println(s.getName)
  }
  //val per = new Person1
  var per:Person1 = new Student
  //
  var student:Student = null
  if(per.isInstanceOf[Student])
    student=per.asInstanceOf[Student]
  println(student.getScore)
}

注意:
(1)Override和super
Scala中,如果子类要覆盖一个父类中的非抽象方法,则必须使用override关键字,override还可以覆盖变量
此外,在子类覆盖父类方法之后,如果我们在子类中就是要调用父类的被覆盖的方法呢?那就可以使用super关键字,显式地指定要调用父类的方法

(2)父类子类转换
isInstanceOf和asInstanceOf

if(per.isInstanceOf[Stus])
stus = per.asInstanceOf[Stus]
println(stus)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值