Scala 之面向对象编程

1. 类

1.1 类的创建

// 方式一
class A{
    // 方法和字段的定义
}

// 方式二
new A

1.2 定义字段和方法

object ClassTest {
  def main(args: Array[String]): Unit = {
    // Scala在调用无参方法时,是可以省略方法名后面的圆括号的
    val c = new Counter	// 创建对象
    println(c.current())	// 方法调用
    c.increment()
  }
}

class Counter {
  // 私有字段,内内部使用
  private var price = 12;

  def increment(): Unit = {
    price += 1
    println("价格为:" + price)
  }

  def current(): Int = {
    price
  }
}
  • private 修饰的字段为私有变量,只能在类内部使用
  • Unit 表示该方法没有返回值,类似于 Javavoid
  • scala 方法返回值不需要写 return,方法最后一个表达式的值就是返回值

简写

class Counter {
  // 私有字段,内内部使用
  private var price = 12;
  
  // 省略大括号
  def increment(): Unit = price + 1 
  
  // 省略返回类型
  def increment2() {price+1}

  //  def increment(): Unit = {
  //    price += 1
  //    println("价格为:" + price)
  //  }
  //
  //  def current(): Int = {
  //    price
  //  }
}

1.3 编译和执行

三种执行方式

  • 将你在 ide 写好的 projectscala下进行编译,之后通过Main调用

  • 打包整个项目为 jar,通过scala -classpath加载后,在scalaimport进行调用

  • 去掉package声明,并且将依赖包通过scala -classpath加载后,再使用:load 加载你的类

参数解释

  • scalacscala 的编译器

  • -classpath:指明外部依赖包

  • -d:指明 编译后的输出文件 要放到哪里,这里把编译结果放在classes文件下

  • 最后指明需要编译的 scala 文件

终端中编译执行

// 切换 scala 程序所在目录,编译,编译成功生成 ClassTest$.class、ClassTest.class、Counter.class 三个文件
F:\JavaStudy\ScalaStudy\src>scalac ClassTest.scala

// 执行
F:\JavaStudy\ScalaStudy\src>scala ClassTest
12
价格为:13

// 示例
// 编译
scalac -classpath lib/spark-assembly-1.5.1-hadoop2.6.0.jar -d classes src/com/xzj/process/*

// 执行
scala -classpath classes:lib/spark-assembly-1.5.1-hadoop2.6.0.jar com.xzj.process.Main

scala 交互解释器中调用

scala> :load F:\JavaStudy\ScalaStudy\src\ClassTest.scala                  
Loading F:\JavaStudy\ScalaStudy\src\ClassTest.scala...                    
defined object ClassTest                                                  
defined class Counter                                                     
                                                                          
scala> val c = new Counter                                                
c: Counter = Counter@c4ed84                                               
                                                                          
scala> c.current()                                                        
res0: Int = 12                                                            
                                                                          
scala> println(c.current())                                               
12                                                                        
                                                                          
scala> c.increment()                                                      
价格为:13                                                               

1.4 getter和setter

Java 中有时候经常把一些字段定义为private类型来完成封装,这样外界就无法访问。如果外界访问或者修改该字段的时候,只能通过该字段提供的gettersetter方法来实现。但是在Scala中是没有gettersetter一说的,但是可以用 value 和 value_= 来代替:

class CounterTest{
  private var privateValue = 0;//私有变量,外界无法直接访问
  def value = privateValue;//定义一个方法,方法名为我们想要的字段的名称,代替getter
  
  // value_= 是方法名字
  def value_= ( newValue : Int ){
    privateValue =  newValue
  }
}
object MyCounter{
  def main(args:Array[String]){
    val myCounter = new CounterTest
    println(myCounter.value);//调用value方法访问value,相当于getter
    myCounter.value = 3 ;//为value设置新值,相当于setter
    println(myCounter.value)
  }
}

注意:setter 是用 value_= 代替的,下划线和等号中间没有空格,没有空格,没有空格!

参考文章:【scala】getter和setter

1.5 辅助构造器

  • 辅助构造器名称为 this,可以有多个辅助构造器
  • 每个辅助构造器必须调用一个一已定义的主或辅助构造器
object MyCounter2 {
  def main(args: Array[String]): Unit = {
      // 不同的参数调用不同的构造器
    val c1 = new Counter    // 主构造器
    val c2 = new Counter("rose")  // 第一个辅助构造器
    val c3 = new Counter("lila", 2) // 第二个辅助构造器
    c1.info()
    c2.info()
    c3.info()

  }
}

class Counter {
  private var value = 0
  private var name = "tom"
  private var mode = 1

  // 第一个辅助构造器
  def this(name: String) {
    this() // 调用主构造器
    this.name = name
  }

  // 第二个辅助构造器
  def this(name: String, mode: Int) {
    this(name) // 调用第一个辅助构造器
    this.mode = mode
  }

  def info(): Unit = {
    printf("Name is %s, mode is %s\n", name, mode)
  }
}

运行结果:

Name is tom, mode is 1
Name is rose, mode is 1
Name is lila, mode is 2

1.6 主构造器

Scala 的主构造器与 Java 不一致,其参数要定义在类名称后面:

object MyCounter2 {
  def main(args: Array[String]): Unit = {
    // 主构造器
    val c4 = new MainCounter("rose", 1)
    c4.info()
  }
}

// 主构造器
class MainCounter(name: String, mode: Int) {
  def info(): Unit = {
    printf("name is %s, mode is %s", name, mode)
  }
}

运行结果:

name is rose, mode is 1

2. 对象

2.1 单例对象

object SingletonObject {
  def main(args: Array[String]): Unit = {
    println("单例对象:first: " + TestSingletonObject.newNum())
    println("单例对象:second: " + TestSingletonObject.newNum())
    println("单例对象:third: " + TestSingletonObject.newNum())

    // 普通对象
    val c1 = new CommonObject
    val c2 = new CommonObject
    val c3 = new CommonObject

    println("普通对象:" + c1.newNum())
    println("普通对象:" + c2.newNum())
    println("普通对象:" + c3.newNum())
  }

}

// 单例对象
object TestSingletonObject {
  private var num = 0

  def newNum() = {
    num += 1
    num
  }
}

// 普通对象
class CommonObject {
  private var num = 0

  def newNum() = {
    num += 1
    num
  }
}

运行结果:

单例对象:first: 1
单例对象:second: 2
单例对象:third: 3
普通对象:1
普通对象:1
普通对象:1
  • 单例对象:不管调用多少次都是上一个对象,所以上面代码会一直累加
  • 普通对象:每次创建新的对象,所以每次都为 1

2.2 伴生对象

若想同时用到包含实例方法和静态方法的类,可以用伴生对象实现,其特点如下:

  • 单例对象与某个类具有相同名称时,它就被称为这个类的伴生对象
  • 类与它的伴生对象必须存在同一文件中,且可以相互访问私有成员(字段和方法)
// 它是 TestObjectAndSample 类的伴生对象
object TestObjectAndSample {
  private var lastId = 0

  // 实现了静态方法类似的功能,类名.方法
  private def newId() = {
    lastId += 1
    lastId
  }

  def main(args: Array[String]): Unit = {
    val t1 = new TestObjectAndSample("rose")
    val t2 = new TestObjectAndSample("lila")
    t1.info()
    t2.info()
  }
}

class TestObjectAndSample {
  private val id = TestObjectAndSample.newId() // 调用伴生对象的私有方法

  private var name = ""

  def this(name: String) {
    this()
    this.name = name
  }

  def info() = {
    printf("the id is %d, the name is %s\n", id, name)
  }
}
the id is 1, the name is rose
the id is 2, the name is lila

2.3 应用程序对象

每个 Scala 应用程序都必须从一个对象的main方法开始,要运行应用程序有两种方式:

  • 直接用 scala xxx.scala 运行:适用于只有一个单例对象的程序,没有定义其他类
  • 先编译再运行
// HelloWorld.scala
// 只有一个单例对象,没有其他类
object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello World!")
  }
}


// 方法一
F:\JavaStudy\ScalaStudy\src>scala HelloWorld.scala
Hello World!

// 方法二:先编译再运行,编译成功生成两个文件:HelloWorld$.class、HelloWorld.class
F:\JavaStudy\ScalaStudy\src>scalac HelloWorld.scala

F:\JavaStudy\ScalaStudy\src>scala HelloWorld
Hello World!

2.4 apply 方法和update 方法

符合以下约定时,将会隐式地调用 apply()、update() 方法:

  • 用括号传递给变量(对象)一个或多个参数时,Scala 会将其转换成对 apply 方法的调用

  • 当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的 update 方法,调用时,是把括号中的参数和等号右边的对象一起作为 update 方法的输入参数来执行调用

2.4.1 apply 方法

object ApplyAndUpdate {
  def main(args: Array[String]): Unit = {
    val tac = new TestApplyClass
      // 给对象传递了一个参数,内部直接调用了类的 apply 方法
    println(tac("rose"))
  }
}

class TestApplyClass {
  def apply(name: String): String = {
    println("TestApplyClass 调用了 apply 方法!!!")
    "Hello " + name
  }
}

输出结果:

TestApplyClass 调用了 apply 方法!!!
Hello rose

单例对象 apply() 方法

object ApplyAndUpdate {
  def main(args: Array[String]): Unit = {
//    val tac = new TestApplyClass
//    println(tac("rose"))

    // 单例对象,将返回值赋值给 tsac
    val tsac = TestSingleApplyClass("lila")
    println(tsac)
  }
}

class TestApplyClass {
  def apply(name: String): String = {
    println("TestApplyClass 调用了 apply 方法!!!")
    "Hello " + name
  }
}

object TestSingleApplyClass {
  def apply(name: String): String = {
    println("TestSingleApplyClass 调用了 apply 方法!!!")
    "Hello " + name
  }
}

伴生对象

object TestAndClassObject {
  def main(args: Array[String]): Unit = {
    val a = ApplyTest() // 调用伴生对象  apply 方法
    a.otherMethod // 返回类 ApplyTest 的实例对象,所有可以直接调用 otherMethod() 方法
    a() // 调用伴生类中的 apply 方法
  }
}

class TestAndClassObject {

}

class ApplyTest {
  def apply() = {
    println("class ApplyTest apply is called!!!")
  }

  def otherMethod: Unit = {
    println("class ApplyTest otherMethod is called!!!")
  }
}

object ApplyTest {
  def apply() = {
    println("object ApplyTest apply is called!!!")
    new ApplyTest() // 返回类 ApplyTest 的实例对象
  }
}

输出结果:

object ApplyTest apply is called!!!
class ApplyTest otherMethod is called!!!
class ApplyTest apply is called!!!

2.4.2 update 方法

除了自定义的 apply 方法,还有一些内置的类在使用时,也会隐式地调用 apply/update 方法,如:

  • 数组对象 Array 在初始化时不需要 new 关键字、不需要构造器,直接传入参数就可以初始化,其内部调用的是 apply 方法: val aArr = Array("rose", "lila", "john")
  • 数组对象 Array 添加值,内部调用的 update 方法
scala> val bArr = new Array[String](3)
bArr: Array[String] = Array(null, null, null)

scala> bArr(0) = "rose"

scala> bArr
res1: Array[String] = Array(rose, null, null)

scala> bArr(1) = "lila"

scala> bArr(2) = "john"

scala> bArr
res4: Array[String] = Array(rose, lila, john)

当对带有括号并包括一到若干参数的对象进行赋值时,编译器将调用对象的 update 方法,调用时,是把括号中的参数和等号右边的对象一起作为 update 方法的输入参数来执行调用

3. 继承

3.1 Scala 与 Java 继承的区别

区别:

  • 重写一个非抽象方法必须使用 override 修饰符
  • 只有主构造器可以调用超类的主构造器
  • 在子类中重写超类的抽象方法时,不需要使用 override 关键字。
  • 可以重写超类中的字段。

相同:

  • 都不允许类从多个超类继承

3.2 抽象类

抽象类特点:

  • 抽象类不能直接实例化

  • 定义抽象类需要使用 abstract 关键字

  • 抽象方法定义不需要 abstract ,只需把方法体空着就行

  • 抽象字段定义:只要不初始化值就是一个抽象字段,但是必须定义字段类型,否则编译报错

abstract class Car {
  val carBand: String // 字段没有初始化,就是抽象字段
  def info() // 抽象方法
  def abc(): Unit = {
    println("Welcome to my Car!!!")
  }
}

3.3 拓展类

继承(拓展)使用 extends 关键字:

  • 重写超类抽象字段,要加 override
  • 重写超类抽象方法,不用加 override
  • 重写超类普通方法,要加 override
object AbstractClass {
  def main(args: Array[String]): Unit = {
    val bmw = new BMWCar
    bmw.info()
    bmw.abc()
  }
}

// 抽象类不能直接实例化
abstract class Car {
  val carBand: String // 字段没有初始化,就是抽象字段
  def info() // 抽象方法
  def abc(): Unit = {
    println("Welcome to my Car!!!")
  }
}

// 拓展类
class BMWCar extends Car {
  // 重写超类抽象字段,要加 override
  override val carBand = "BMW"

  // 重写超类抽象方法,不用加 override
  def info(): Unit = {
    println("This is my BMW Car!!!")
  }

  // 重写超类普通方法,要加 override
  override def abc(): Unit = {
    println("Welcome to my BMW Car!!!")
  }
}

运行结果:

This is my BMW Car!!!
Welcome to my BMW Car!!!

4. 特质

Scala 的特质类似于 Java 的接口,是代码重用的基本单元,可以同时拥有抽象方法和具体方法。另外通过特质可实现 多重继承

  • 定义特质:trait 关键字
  • 抽象方法不需要 abstract 关键字
  • 没有方法体的的方法即为抽象方法
trait ClassName {
    var id: Int			// 抽象字段
    def funName(): Int	// 抽象方法
}

4.1 特质混入类中

将特质混入类中,实现类继承的功能(若特质只实现抽象字段、抽象方法就类似于 Java 接口):

object TraitClass {
  def main(args: Array[String]): Unit = {
    val bmw = new BMWCarId
    println(bmw.info())
    val byd = new BYDCarId
    println(byd.info())
  }
}

// 特质
trait CarId {
  var id: Int	// 抽象字段

  def info(): Int	// 抽象方法
}

class BMWCarId extends CarId {
  override var id = 10001

  def info(): Int = {
    id + 1
  }
}

class BYDCarId extends CarId {
  override var id = 10002

  override def info(): Int = {
    id + 1
  }
}

运行结果:

10002
10003

4.2 特质中实现具体方法

除了可以实现抽象字段和抽象方法外,特质中还可以实现具体的的方法:

object TraitClass {
  def main(args: Array[String]): Unit = {
    val bmw = new BMWCarId
    println(bmw.info())
    val byd = new BYDCarId
    println(byd.info())

    bmw.carName("BMW")
  }
}

// 特质
trait CarId {
  var id: Int

  def info(): Int

  def carName(name: String): Unit = {
    println("Car name: " + name)
  }
}

4.3 多个特质实现多重继承

一个类可以混入多个特质,使其实现多重继承的功能,使用方法:使用extends关键字混入第1个特质,后面可以反复使用with关键字混入更多特质

示例:

object TestTrait {
  def main(args: Array[String]): Unit = {
    val bmw = new BMW
    bmw.info()
    bmw.greeting("Welcome my second car.")
    bmw.color("My Car color is black.")
  }
}

trait CarIdTest {
  var id: Int

  def info(): Int
}

trait CarGreeting {
  def greeting(name: String): Unit = {
    println(name)
  }
}

trait CarColor {
  def color(name: String): Unit = {
    println(name)
  }
}

class BMW extends CarIdTest with CarGreeting with CarColor {
  override var id: Int = 10001

  def info(): Int = {
    id + 1
  }

  // 重写普通方法
  override def greeting(name: String): Unit = super.greeting(name)

  override def color(name: String): Unit = super.color(name)
}

运行结果:

Welcome my second car.
My Car color is black.

5. 模式匹配

模式匹配类似于 Javaswitch-case 语句,但是功能更为强大,可应用到 switch 语句、类型检查、结构等:

5.1 简单匹配

以下为一个简单的匹配整型的实例:

object TestMatch {
  def main(args: Array[String]): Unit = {
    val tm = new TestMatch

    // 整型匹配
    tm.intMatch()
  }
}

class TestMatch {
  def intMatch(): Unit = {
    val score = 90
    val info = score match {
      case 70 => "D"
      case 80 => "C"
      case 90 => "B"
      case 100 => "A"
      case _ => score + "是不及格"    // 没匹配返回的信息,这里还可以直接使用当前匹配的变量
    }
    println(info)
  }
}

5.2 类型匹配

object TestMatch {
  def main(args: Array[String]): Unit = {
    val tm = new TestMatch

    // 表达式匹配
    tm.elemMatch()
  }
}

class TestMatch {
  def elemMatch(): Unit = {
    for (elem <- List(1, 2.2, "Scala", "Python", "Java")) {
      val str = elem match {
        case i: Int => i + " is an Int Value"
        case d: Double => d + " is an Double Value"
        case "Scala" => "Scala is found"
        case s: String => s + " is a String Value"
        case _ => "This is an unexpected Value"
      }
      println(str)
    }
  }
}

输出结果:

1 is an Int Value
2.2 is an Double Value
Scala is found
Python is a String Value
Java is a String Value

5.3 guard 守卫语句

相比于 JavaScalaif 增加了两项强大的功能,一是可以直接作为赋值语句,另一种功能是作为守卫语句

if 作为赋值语句

作为赋值语句,替代了三元运算符,且支持逻辑语句块:

object IfTest {
  def main(args: Array[String]): Unit = {
    val (a, b) = (10, 22)
    val value = if (a > b) 1 else if (a == b) {
      val c = a - 1
      println(c)
    } else 0
    println(value)
  }
}

if作为for守卫语句

scala> val items = for (i <- -1 to 26 if i % 2 == 0 if i > 5) yield i
items: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26)

scala> items
res0: scala.collection.immutable.IndexedSeq[Int] = Vector(6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26)

scala> val items = for (i <- -1 to 26 if i % 2 == 0 if i > 5) yield i *3
items: scala.collection.immutable.IndexedSeq[Int] = Vector(18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78)

if 作为 for 守卫语句具有以下特点:

  • 一个 for 循环可以支持多个 if 语句,以空格、回车字符分割
  • 多个 if 语句之间的关系是逻辑与&&
  • if 守卫语句并不支持其他循环语句,如:while
object TestMatch {
  def main(args: Array[String]): Unit = {
    val tm = new TestMatch

    // 守卫 guard 语句
    tm.guardMatch()
  }
}

class TestMatch {
    // 判断奇偶
  def guardMatch(): Unit = {
    for (elem <- List(1, 2, 3, 4, 5, 6, 7, 8)) {
      elem match {
        case _ if (elem % 2 == 0) => println(elem + " is even.")

        case _ => println(elem + " is odd.")
      }
    }

  }

}

if 作为 case 守卫语句

object IfTest {
  def main(args: Array[String]): Unit = {
    val items = for (i <- -1 to 26) yield i
    val xs = items match {
      case x if x.size < 1 => null
      case x if x.contains(8) => 8
    }
    println(xs)
  }
}

对于case语句,虽然没有显式的 break 语句,但是依旧是“满足即阻断”,只会执行最先满足的 case

5.4 其他模式匹配

其他模式匹配还包括:

  • for 表达式中的模式
  • case 类的匹配
  • option 类型

5.4.1 for 表达式

scala> val info = Map("name" -> "rose", "age" -> 18)
info: scala.collection.immutable.Map[String,Any] = Map(name -> rose, age -> 18)

scala> for ((k, v) <- info) println(k, v)
(name,rose)
(age,18)

scala> for ((kv) <- info) println(kv._1, kv._2)
(name,rose)
(age,18)

5.4.2 case 类

case 类是一种特殊的类,它们经过优化以被用于模式匹配:

object IfTest {
  def main(args: Array[String]): Unit = {
    case class Animal(color: String, age: Int)

    val fish = new Animal("white", 1)
    val duck = new Animal("black", 2)
    val dog = new Animal("yellow", 3)
    for (item <- List(fish, duck, dog)) {
      item match {
        case Animal("white", 1) => println("This is a fish!")
        case Animal("black", 2) => println("This is a duck!")
        case Animal("yellow", 3) => println("This is a dog!")
        case _ => println("no animal")
      }
    }
  }
}

5.4.3 option 类

Option 类型用case类来表示那种可能存在、也可能不存在的值,Option 有个 some 子类,当被引用的对象不存在时,可以使用 some 子类返回默认值,而不是无意义的 NoneNull

Option[T] 是一个类型为 T 的可选值的容器: 如果值存在, Option[T] 就是一个 Some[T] ,如果不存在, Option[T] 就是对象 None

object OptionTest {
  def main(args: Array[String]): Unit = {
    val info = Map("name" -> "rose", "age" -> 18)
    println("show name: " + show(info.get("name")))
    println("show gender: " + show(info.get("gender")))

  }

  def show(x: Option[Any]) = x match {
    case Some(s) => s
    case None => "no such field"
  }
}

运行结果:

show name: rose
show gender: no such field

getOrElse() 方法

scala> val info = Map("name" -> "rose", "age" -> 18)
info: scala.collection.immutable.Map[String,Any] = Map(name -> rose, age -> 18)

// 不存在的 键返回 None 对象,若取到了则放在 some 中返回
scala> val gender = info.get("gender")
gender: Option[Any] = None

// 指定默认值,使在获取不存在 key 时不返回 None 对象
scala> gender.getOrElse("info no gender")
res6: Any = info no gender

scala> println(gender.getOrElse("info no gender"))
info no gender

Option 实际上是一个容器,可以看做为一个集合,只不过是这个集合值包含一个元素(被包装在 Some 中返回),因为集合的一些常用也可以使用,如:map、foreach、filter 等:

scala> info.get("gender").foreach(println)

isEmpty 方法

isEmpty() 方法来检测元组中的元素是否为 None

object OptionTest {
  def main(args: Array[String]): Unit = {
    val a: Option[Int] = Some(22)
    val b: Option[Int] = None
    println(a)		// Some(22)
    println(b)		// None

    println(a.getOrElse(0)) //22
    println(b.getOrElse(26)) //26
    println(a.isEmpty) // false
    println(b.isEmpty) // true

  }
}

Scala Option 常用方法

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风老魔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值