scala中高阶函数、类,对象与继承

高阶函数

作为参数的函数

函数可以作为一个参数传入到一个方法当中去

def main(args: Array[String]): Unit = {
 val myFunc1 =(x:Int) =>{
   x * x
 }
 val myArray = Array(1,3,5,7,9).map(myFunc1)
  println(myArray.mkString(","))
}

匿名函数

没有名字的函数即是匿名函数,我们可以通过函数表达式来设置匿名函数

def main(args: Array[String]): Unit = {
  println((x:Int,y:String) => x + y)
}

高阶函数

  1. 能够接受函数作为参数的方法,叫做高阶函数
def main(args: Array[String]): Unit = {
  val func3:(Int,String) =>(String,Int)={
    (x,y)=>(y,x)
  }
  def myMethod3(hello:(Int,String) => (String,Int)):Int ={
    val resultFunc = hello(20,"hello")
    resultFunc._2
  }
  println(myMethod3(func3))
}
  1. 高阶函数同样可以返回一个函数类型
def main(args: Array[String]): Unit = {
  def myFunc4(x:Int) = (y:String) => y
  println(myFunc4(50))
}

参数类型推断

def main(args: Array[String]): Unit = {
  val array  = Array(1,2,3,4,5,6,7,8,9)
  //map当中需要传入一个函数,我们可以直接定义一个函数
  array.map((x:Int) => x * 2 )
  //进一步化简 参数推断省去类型信息
  array.map((x) => x * 2 )
  //进一步化简  单个参数可以省去括号
  array.map( x => x * 2 )
  //进一步化简  如果变量只在=>右边只出现一次,可以用_来代替
  array.map( 2 * _ )

}

闭包与柯里化

柯里化存在的意义是什么???注意:柯里化是scala当中面向函数式编程导致的一种必然的结果,最终推导而来产生的一种现象

def main(args: Array[String]): Unit = {
  //柯里化的定义形式
  def kery(x:Int)(y:Int):Int={
    x + y
  }
  println(kery(3)(5))
  //柯里化的推导过程,注意方法的返回值不要定义任何的返回值类型
val keryResult = (x:Int) => (y:Int) => {x + y}

  def keryMethod(x:Int) ={
    (y:Int) => x+ y
  }
  println(keryMethod(20))
  println(keryMethod(20)(10))
  //注意,方法当中的函数,调用了方法的参数,就叫做闭包
}

再来看一个案例

/**
  * 柯里化的应用,比较数组集合当中两个对应下标字符串是否相等
  * @param args
  */
def main(args: Array[String]): Unit = {
  val a = Array("Hello", "World")
  val b = Array("hello", "world")
  println(a.corresponds(b)(_.equalsIgnoreCase(_)))

}

scala当中的类

类的定义与创建

创建一个scala class来定义我们的一个类。类当中可以定义各种属性或者方法,或者函数都可以

class Person {
  //定义一个属性,叫做name的,使用val不可变量来进行修饰
  // 用val修饰的变量是可读属性,有getter但没有setter(相当与Java中用final修饰的变量)
  val name:String ="zhangsan"
  //定义一个属性,叫做age的,使用var可变量来进行修饰
  //用var修饰的变量都既有getter,又有setter
  var age:Int = 28

  //类私有字段,只能在类的内部使用或者伴生对象中访问
  private val  address:String = "地球上"

  //类私有字段,访问权限更加严格的,该字段在当前类中被访问
  //在伴生对象里面也不可以访问
  private[this] var pet = "小强"

  //在类当中定义了一个方法,
  def hello(first:Int,second:String):Int ={
    println(first+"\t"+second)
    250
  }
  /**
    * 定义了一个函数
    */
  val func1 =(x:Int,y:Int) =>{
    x+y
  }
}

类的实例化以及使用

如果想要使用类的话,那么REPL就满足不了我们的要求了,我们重新创建一个对应的Object的scala文件

object ScalaClass {
  def main(args: Array[String]): Unit = {
    //创建对象两种方式。这里都是使用的无参构造来进行创建对象的
    val person = new Person
    val person1 = new Person()
    //注意,我们可以使用对象的属性加上_= 给var修饰的属性进行重新赋值
    //其实就是调用set方法,方法名叫做 age_=    
    person.age_= (50)
    //直接调用类的属性,其实就是调用get方法
    println(person.age)
    println(person.hello(50,"helloworld"))
    val func = person.func1(10,20)
    println(func)
    println("============")
  }
}

属性的getter和setter方法

对于scala类中的每一个属性,编译后,会有一个私有的字段和相应的getter、setter方法生成

//getter方法
println(person age)
//setter方法
println(person age_= (18))
//getter方法
println(person.age)

当然了,你也可以不使用自动生成的方式,自己定义getter和setter方法

class Dog2 {
  private var _leg = 4
  def leg = _leg
  def leg_=(newLeg: Int) {
    _leg = newLeg
  }
}

使用之:

val dog2 = new Dog2
dog2.leg_=(10)
println(dog2.leg)

规范提示:自己手动创建变量的getter和setter方法需要遵循以下原则:

  1. 字段属性名以“_”作为前缀,如:_leg
  2. getter方法定义为:def leg = _leg
  3. setter方法定义时,方法名为属性名去掉前缀,并加上后缀,后缀是:“leg_=”,如例子所示

类的构造器

scala当中类的构造器分为两种:主构造器和辅助构造器
scala当中规定,所有的辅助构造器,最后都必须调用另外一个构造器,另外一个构造器可以是辅助构造器,也可以是主构造器

//主构造器,直接定义在类上面
class Dog (name:String,age:Int){

  //在scala当中,可以直接将代码写在class当中,而在java当中,
  //代码必须包含在方法当中。
  //其实在scala当中,虽然你把代码写在了Class类当中,经过编译之后,
  //class类的代码都进入到了主构造器方法当中去了
  println(name)
  println(age)

  var gender:String = "";

   def this(name:String,age:Int,gender:String){
    //每个辅助构造器,都必须以其他辅助构造器,或者主构造器的调用作为第一句
    this(name:String,age:Int)
    this.gender = gender
  }

  var color ="";

  /**
    * 我们也可以通过private来进行修饰我们的构造器,
    * @param name
    * @param age
    * @param color
    * @param gender
    */
  private def this(name:String,age:Int,color:String,gender:String){
    this(name:String,age:Int)
    this.color = color
  }

}

scala当中的对象

scala当中的Object

在scala当中,没有类似于像java当中的static修饰的静态属性或者静态方法或者静态代码块之类的,但是我们可以通过scala当中的Object来实现类似的功能。可以理解为scala当中的Object里面的属性或者方法都是静态的,可以直接调用
定义一个class类,然后在class类当中定义一个Object的对象。object对象当中的所有属性或者方法都是静态的

class Session {
  def hello(first:Int):Int={
    println(first)
    first
  }
}
object SessionFactory{
  val session = new Session
  def getSession():Session ={
    session
  }
  def main(args: Array[String]): Unit = {

    for(x <- 1 to 10){
      //通过直接调用,产生的对象都是单列的
      val session = SessionFactory.getSession()
      println(session)
    }
  }
}

伴生类与伴生对象

  • 如果有一个class文件,还有一个与class同名的object文件,那么就称这个object是class的伴生对象,class是object的伴生类;
  • 伴生类和伴生对象必须存放在一个.scala文件中;
  • 伴生类和伴生对象的最大特点是,可以相互访问;
  • 举例说明:
class ClassObject {
  val id = 1
  private var name = "itcast"
  def printName(): Unit ={
    //在Dog类中可以访问伴生对象Dog的私有属性
    println(ClassObject.CONSTANT + name )
  }


}

object ClassObject{
  //伴生对象中的私有属性
  private val CONSTANT = "汪汪汪 : "
  def main(args: Array[String]) {
    val p = new ClassObject
    //访问私有的字段name
    p.name = "123"
    p.printName()
  }
}

scala当中的apply方法

  • object 中非常重要的一个特殊方法,就是apply方法;
  • apply方法通常是在伴生对象中实现的,其目的是,通过伴生类的构造函数功能,来实现伴生对象的构造函数功能;
  • 通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,…参数n)时apply方法会被调用;
  • 在创建伴生对象或伴生类的对象时,通常不会使用new class/class() 的方式,而是直接使用 class(),隐式的调用伴生对象的 apply 方法,这样会让对象创建的更加简洁;

举例说明:

class ApplyObjectClass (name:String){
  println(name)
}
object ApplyObjectClass{
  
  def apply(name:String): ApplyObjectClass = {
    new ApplyObjectClass(name)
  }
  def main(args: Array[String]): Unit = {
    //调用的apply方法来创建对象
    val applyObjectClass = ApplyObjectClass("lisi")
    //调用的是new  Class来创建对象
    val applyObjectClass2 =new ApplyObjectClass("wangwu")
  }
}

scala当中的main方法

  • 同Java一样,如果要运行一个程序,必须要编写一个包含 main 方法的类;
  • 在 Scala 中,也必须要有一个 main 方法,作为入口;
  • Scala 中的 main 方法定义为 def main(args: Array[String]),而且必须定义在 object 中;
  • 除了自己实现 main 方法之外,还可以继承 App Trait,然后,将需要写在 main 方法中运行的代码,直接作为 object 的 constructor 代码即可,而且还可以使用 args 接收传入的参数;

案例说明:

//1.在object中定义main方法
object Main_Demo1 {
  def main(args: Array[String]) {
    if(args.length > 0){
      println("Hello, " + args(0))
    }else{
      println("Hello World1!")
    }
  }
}
//2.使用继承App Trait ,将需要写在 main 方法中运行的代码
// 直接作为 object 的 constructor 代码即可,
// 而且还可以使用 args 接收传入的参数。

object Main_Demo2 extends App{
  if(args.length > 0){
    println("Hello, " + args(0))
  }else{
    println("Hello World2!")
  }
}

枚举

Scala中没有枚举类型,但是我们可以退通过定义一个扩展Enumeration类的对象,并以value调用初始化枚举中的所有可能值

scala当中的继承

Scala中继承(extends)的概念

  • Scala 中,让子类继承父类,与 Java 一样,也是使用 extends 关键字;
  • 继承就代表,子类可继承父类的 field 和 method ,然后子类还可以在自己的内部实现父类没有的,子类特有的 field 和method,使用继承可以有效复用代码;
  • 子类可以覆盖父类的 field 和 method,但是如果父类用 final 修饰,或者 field 和 method 用 final 修饰,则该类是无法被继承的,或者 field 和 method 是无法被覆盖的。
  • private 修饰的 field 和 method 不可以被子类继承,只能在类的内部使用;
  • field 必须要被定义成 val 的形式才能被继承,并且还要使用 override 关键字。 因为 var 修饰的 field 是可变的,在子类中可直接引用被赋值,不需要被继承;即 val 修饰的才允许被继承,var 修饰的只允许被引用。继承就是改变、覆盖的意思。
  • Java 中的访问控制权限,同样适用于 Scala

举例:

package cn.itcast.extends_demo

class Person {
  val name="super"
  def getName=this.name
}
class Student extends Person{
  //继承加上关键字
  override
  val name="sub"
  //子类可以定义自己的field和method
  val score="A"
  def getScore=this.score
}

Scala中override 和 super 关键字

  • Scala中,如果子类要覆盖父类中的一个非抽象方法,必须要使用 override 关键字;子类可以覆盖父类的 val 修饰的field,只要在子类中使用 override 关键字即可。
  • override 关键字可以帮助开发者尽早的发现代码中的错误,比如, override 修饰的父类方法的方法名拼写错误。
  • 此外,在子类覆盖父类方法后,如果在子类中要调用父类中被覆盖的方法,则必须要使用 super 关键字,显示的指出要调用的父类方法。

举例:

class Person1 {
  private val name = "leo"
  val age=50
  def getName = this.name
}
class Student1 extends Person1{
  private val score = "A"
  //子类可以覆盖父类的 val field,使用override关键字
  override
  val age=30
  def getScore = this.score
  //覆盖父类非抽象方法,必须要使用 override 关键字
  //同时调用父类的方法,使用super关键字
  override def getName = "your name is " + super.getName
}

Scala中isInstanceOf 和 asInstanceOf

如果实例化了子类的对象,但是将其赋予了父类类型的变量,在后续的过程中,又需要将父类类型的变量转换为子类类型的变量,应该如何做?

  • 首先,需要使用 isInstanceOf 判断对象是否为指定类的对象,如果是的话,则可以使用 asInstanceOf 将对象转换为指定类型;
  • 注意: p.isInstanceOf[XX] 判断 p 是否为 XX 对象的实例;p.asInstanceOf[XX] 把 p 转换成 XX 对象的实例
  • 注意:如果没有用 isInstanceOf 先判断对象是否为指定类的实例,就直接用 asInstanceOf 转换,则可能会抛出异常;
  • 注意:如果对象是 null,则 isInstanceOf 一定返回 false, asInstanceOf 一定返回 null;
  • Scala与Java类型检查和转换
    在这里插入图片描述
    举例:
package cn.itcast.extends_demo

class Person3 {}
class Student3 extends Person3
object Student3{
    def main (args: Array[String] ) {
    val p: Person3 = new Student3
    var s: Student3 = null
    //如果对象是 null,则 isInstanceOf 一定返回 false
    println (s.isInstanceOf[Student3])
    // 判断 p 是否为 Student3 对象的实例
  if (p.isInstanceOf[Student3] ) {
    //把 p 转换成 Student3 对象的实例
      s = p.asInstanceOf[Student3]
  }
  println (s.isInstanceOf[Student3] )
  }
}

Scala中getClass 和 classOf

  • isInstanceOf 只能判断出对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象;
  • 如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 了;
  • p.getClass 可以精确地获取对象的类,classOf[XX] 可以精确的获取类,然后使用 == 操作符即可判断;

举例:

package cn.itcast.extends_demo

class Person4 {}
class Student4 extends Person4
object Student4{
  def main(args: Array[String]) {
    val p:Person4=new Student4
    //判断p是否为Person4类的实例
    println(p.isInstanceOf[Person4])//true
    //判断p的类型是否为Person4类
    println(p.getClass == classOf[Person4])//false
    //判断p的类型是否为Student4类
    println(p.getClass == classOf[Student4])//true
  }
}

Scala中使用模式匹配进行类型判断

  • 在实际的开发中,比如 spark 源码中,大量的地方使用了模式匹配的语法进行类型的判断,这种方式更加地简洁明了,而且代码的可维护性和可扩展性也非常高;

  • 使用模式匹配,功能性上来说,与 isInstanceOf 的作用一样,主要判断是否为该类或其子类的对象即可,不是精准判断。

  • 等同于 Java 中的 switch case 语法;

举例:

package cn.itcast.extends_demo

class Person5 {}
class Student5 extends Person5
object Student5{
  def main(args: Array[String]) {
    val p:Person5=new Student5
    p match {
      // 匹配是否为Person类或其子类对象
      case per:Person5 => println("This is a Person5's Object!")
      // 匹配所有剩余情况
      case _  =>println("Unknown type!")
    }
  }

Scala中protected

  • 跟 Java 一样,Scala 中同样可使用 protected 关键字来修饰 field 和 method。在子类中,可直接访问父类的 field 和 method,而不需要使用 super 关键字;

  • 还可以使用 protected[this] 关键字, 访问权限的保护范围:只允许在当前子类中访问父类的 field 和 method,不允许通过其他子类对象访问父类的 field 和 method。

举例:

package cn.itcast.extends_demo

class Person6{
  protected var name:String="tom"
  protected[this] var hobby:String ="game"
  protected def sayBye=println("再见...")
}
class Student6 extends Person6{
  //父类使用protected 关键字来修饰 field可以直接访问
  def  sayHello =println("Hello "+name)
  //父类使用protected 关键字来修饰method可以直接访问
  def  sayByeBye=sayBye
  def makeFriends(s:Student6)={
    println("My hobby is "+hobby+", your hobby is UnKnown")
  }
}
object Student6{
  def main(args: Array[String]) {
    val s:Student6=new Student6
    s.sayHello
    s.makeFriends(s)
    s.sayByeBye
  }
}

Scala中调用父类的constructor

  • Scala中,每个类都可以有一个主constructor和任意多个辅助constructor,而且每个辅助constructor的第一行都必须调用其他辅助constructor或者主constructor代码;因此子类的辅助constructor是一定不可能直接调用父类的constructor的;

  • 只能在子类的主constructor中调用父类的constructor。

  • 如果父类的构造函数已经定义过的 field,比如name和age,子类再使用时,就不要用 val 或 var 来修饰了,否则会被认为,子类要覆盖父类的field,且要求一定要使用 override 关键字。

举例:

package cn.itcast.extends_demo

class Person7(val name:String,val age:Int){
  var score :Double=0.0
  var address:String="beijing"
  def this(name:String,score:Double)={
    //每个辅助constructor的第一行都必须调用其他辅助constructor或者主constructor代码
    //主constructor代码
      this(name,30)
      this.score=score
  }
  //其他辅助constructor
  def this(name:String,address:String)={
      this(name,100.0)
      this.address=address
  }
}
class Student7(name:String,score:Double) extends Person7(name,score)

Scala中抽象类

  • 如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现不同的方法。此时,可以将父类中的这些方法编写成只含有方法签名,不含方法体的形式,这种形式就叫做抽象方法;
  • 一个类中,如果含有一个抽象方法或抽象field,就必须使用abstract将类声明为抽象类,该类是不可以被实例化的;
  • 在子类中覆盖抽象类的抽象方法时,可以不加override关键字;

举例:

package cn.itcast.extends_demo

abstract class Person9(val name:String) {
  //必须指出返回类型,不然默认返回为Unit
  def sayHello:String
  def sayBye:String
}
class Student9(name:String) extends Person9(name){
  //必须指出返回类型,不然默认
  def sayHello: String = "Hello,"+name
  def sayBye: String ="Bye,"+name
}
object Student9{
  def main(args: Array[String]) {
    val s = new Student9("tom")
    println(s.sayHello)
    println(s.sayBye)
  }
}

Scala中抽象field

如果在父类中,定义了field,但是没有给出初始值,则此field为抽象field;

举例:

package cn.itcast.extends_demo

abstract class Person10 (val name:String){
//抽象fields
    val age:Int
}
class Student10(name: String) extends Person10(name) {
   val age: Int = 50
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值