大数据课程J3——Scala的类定义

文章作者邮箱:yugongshiye@sina.cn              地址:广东惠州

 ▲ 本章节目的

⚪ 了解Scala的柯里化 Currying;

⚪ 掌握Scala的类定义;

⚪ 掌握Scala的样例类、option类;

⚪ 掌握Scala的隐式转换机制;

一、柯里化 Currying

柯里化(Currying)技术 Christopher Strachey 以逻辑学家 Haskell Curry 命名的(尽管它是 Moses Schnfinkel 和 Gottlob Frege 发明的)。它是把接受多个参数的函数变换成接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术

案例1

object Demo08 {

  def main(args: Array[String]): Unit = {

    //首先我们定义一个函数:

    def f1(a:Int,b:Int):Int={a+b}

    //现在我们把这个函数变一下形:

    def f2(a:Int)(b:Int)={a+b}

    //那么我们应用的时候,应该是这样用:f2(2)(3),最后结果都一样是5,这种方式(过程)就叫柯里化。

    val r1=f2(2)(3)

    //柯里化实质上会演变成这样一个函数:

    //接收一个参数a,返回一个匿名函数,

    //该匿名函数又接收一个参数b,函数体为a+b

    def f3(a:Int)=(b:Int)=>a+b

    val f4=f3(2)

    //请思考 f4(3)的值是多少?答案是:5

    f4(3)

  }

}

示例2

object Demo09 {

  def f1(a:Int,b:Int,c:Int):Int={a+b+c}

  def f2(a:Int)(b:Int)(c:Int):Int={a+b+c}

  def f3(a:Int)(b:Int,c:Int):Int={a+b+c}

  def f4(a:Int,b:Int)(c:Int):Int={a+b+c}

  def main(args: Array[String]): Unit = {

    //f1f2的函数的体现的是传入三个数,马上得到结果

    f1(1,2,3)

    f2(1)(2)(3)

    //f3函数则可以体现:延迟执行的思想以及固定易变因素的思想

    val r1=f3(1)(2,3)

  }

}

示例3

object Demo10 {

  def f1(a:Int,b:Int,f:(Int,Int)=>Int):Int={f(a,b)}

  def f2(a:Int)(b:Int)(f:(Int,Int)=>Int):Int={f(a,b)}

  def f3(a:Int,b:Int)(f:(Int,Int)=>Int):Int={f(a,b)}

  def f4(a:Int)(b:Int,f:(Int,Int)=>Int):Int={f(a,b)}

  def main(args: Array[String]): Unit = {

    //调用f3

    f3(2,3){(a:Int,b:Int)=>a+b}

    //可简化为下面的形式,我们发现这和scala中很多函数的形式很相近,比如:for(x<-1 to 10){print(_)}

    //所以,柯里化的另外一个作用是让用户灵活的自义定自建控制结构

    f3(2,3){_+_}

    f3(2,3){_*_}   

 // 延迟处理思想

  def f3(a:Int)=(b:Int)=>a+b

  val f4=f3(2)

   f4(3)

  }

}

二、类——上篇

1. 概述

1. scala中的类和java中基本类似。

2. scala中的类同样通过class来进行声明。

3. scala中的类同样可以具有成员变量和成员方法。

4. scala中的类同样可以通过new关键字来创建出对象。

2. 创建类

示例1

//创建一个类,并定义类里的两个成员变量name和age。以及一个成员方法 eat()

//需要注意的是:scala中变量(var)声明时需要指定初始值,

class Person {

  var name:String="";

  var age:Int=0;

  def eat(){

    println("吃饭")

  }

}

注:成员属性和成员方法默认都是public的 需要私有可以写private 需要保护可以写protected

 示例2

//当成员变量或成员方法是私有属性时,外部将不能直接访问,这个同java一样

class Person {

  private var name:String="";

  private var age:Int=0;

  private def eat(){

    println("吃饭")

  }

}

 def main(args: Array[String]): Unit = {

    val p=new Person

    p.name="tom";

    p.age=1;

    p.eat();

  }

3. 类的构造

和java不同,scala中的类不需要明确声明一个构造器,而是直接将构造参数通过构造参数列表声明为类的一部分。

而直接写在类的体中的既不是类的成员变量也不是成员函数的部分,会自动收集为构造函数的体。

示例3

//scala中的类不需要明确声明一个构造器,而是直接将构造参数通过构造参数列表声明为类的一部分

class Person(var1:String,var2:Int){

  var name=var1;

  var age=var2;

  //而直接写在类的体中的既不是类的成员变量也不是成员函数的部分,会自动收集为构造函数的体。

  println("……")

  println("***")

}

  def main(args: Array[String]): Unit = {

    //当调用构造方法时,会打印 …… 和***

     val p=new Person("tom",23)

  }

4. 辅助构造器

有些时候 一个类里需要多个构造器。scala里主构造器之外的构造器被称为辅助构造器。

1. Scala的辅助构造器定义开始于 def this()

2. Scala里每个辅助构造器的第一个动作都是调用同类的构造器。

示例4

//scala支持辅助构造器,

class Person(var1:String,var2:Int){

  var name=var1;

  var age=var2;

  //Scala的辅助构造器定义开始于 def this()

  //Scala里每个辅助构造器的第一个动作都是调用同类的构造器。

  def this(var1:String){

    this(var1:String,0)

  }

  //Scala的辅助构造器定义开始于 def this()

  def this(var2:Int){

    this("",var2:Int)

  }

}

  def main(args: Array[String]): Unit = {

    val p1=new Person("tom")

    val p2=new Person(23)

  }

 一个完整的示例:

class Person(v1:String,v2:Int){

  private var name=v1

  private var age=v2

  def this(v1:String){

     this(v1:String,0)

  }

  def this(v2:Int){

     this("",v2)

  }

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

  def getName()={name}

  def setAge(age:Int)={this.age=age}

  def getAge()={age}

  def eat()={println("eat")}

  def say()={println("say")}

  println("&&&&&&")

}

5. 单例对象 ( object )

1. scala中的类(class)不能定义静态成员(或静态方法),而代之以定义单例对象来替代。

2. 单例对象需要通过object关键字来声明

3. 一个单例对象可以单独存在,也可以绑定到一个类上

4. 单例对象当中的所有方法,都可以不需要创建对象而直接通过object单例对象的名字直接来调用,用起来感觉就像一个静态方法一样。

5. 当一个单例对象和某个类写在同一个源文件中且共享同一个名字时,他们就产生了一个绑定的关系

6. 此时单例对象称为该类的伴生对象。类称为该对象的伴生类。

7. 类和他的伴生对象可以互相访问其私有成员。

8. 以伴生的方式使为类增加静态成员称为了可能

9. 单例对象不能new,因此也没有构造参数。

10. 可以把单例对象当作是java中可能会用到的静态方法工具类。

11. 比如作为程序的入口main方法必须是静态的,所以main方法必须处在一个单例对象中,而不能写在一个类中。

12. 单例对象在第一次被访问时才会被初始化。

示例5

//类Person

class Person{

  //此私有变量,伴生类可以访问

  private val namespace="1706"

}

//Person的单利对象,也即伴生类Person,

object Person{

  def showTime(){

    println(System.currentTimeMillis())

  }

  def shownamespace(){

    val p=new Person

    //可以访问类的私有变量

    println(p.namespace)

  }

}

 def main(args: Array[String]): Unit = {

    Person.showTime();

    Person.shownamespace()

  }

6. 重写和重载

1. 重写是指覆盖父类中的方法来在子类中做其他事项

override def 父类方法名 参数列表 返回值 方法体

2. 重载是指在同一个类中提供方法名相同但是参数不同的方法和java中基本一致

示例6:

class Student ( v1 : String , v2 : Int ) extends Person (v1, v2) {

override def eat () = { println ( "student eat" ) }

}

7. final的使用

可以用在成员变量、成员方法、类本身上。

作用和java中相同。

三、类——下篇

1. 抽象类

scala中同样支持抽象类的使用,抽象类的内部可以包含抽象方法和非抽象方法。

抽象类不允许被实例化,抽象类主要是用来被继承的。

示例1:

abstract class Teacher{

//抽象方法,返回值为 Unit

  def teach()

//抽象方法,返回值为String

  def makeNote() : String

//这不是一个抽象方法

  def eat(){

  }

}

class WebTeacher extends Teacher {

  def teach() : Unit = {

    println ( "teach web" )

  }

  def teach() : String = {

    "teach web" 

  }

}

2. 支持多态

示例2:

object Demo13{

  def main(args: Array[String]): Unit = {

    val t1:Teacher=new TeacherChen("chen",32)

    val t2:Teacher=new TeacherLiu("liu",32)

  }

3. 特质 trait

1)可以类比java中的接口,但是又和接口非常不一样,特质相当于java中的接口,java中称为类实现了接口,scala中称为混入了特质。

2)和java中的接口不同的是,scala中的特质可以包含具有方法体的方法。

和抽象类不同的地方在于,scala的类只能单继承,但是可以多混入,利用这种方式可以实现类似c语言中多继承的特性。

3)在类中可以通过extends 或 with 关键字来让类混入特质,如果类没有明确继承父类,extends关键字没有被占用就可以使用extends。

4)但是如已经使用了extends显示的继承了父类,再向混入特质就要用with关键字了。

一个类的声明中只能有一个 extends,但是可以有多个with

示例3:

//trait类似于java的接口,但可以做具体方法的实现

trait Drive{

  def piaoyi()

  def feiche(){

  }

}

trait Cook{

  def tudousi()

  def chaojidan()

}

//scala中,只能继承一个父类,但是可以混入多个特质(trait)

//需要实现特质中未实现的方法

//此外,需要注意的是,如果未继承任何类或抽象类,在混入特质时,比如有且仅有一个特质需要用extends来混入,而其他特质用with混入

class XueSheng extends Person with Drive with Cook{

  override def piaoyi(){

  }

  override def tudousi(){

  }

  override def chaojidan(){

  }

}

4. 泛型

基本和java中相同,不同的是,泛型是用方括号引起来的。

val arr = Array[String](); 

5. lazy 懒值

正常情况下通过val 和 var定义的量都会直接分配空间,即使这个量要在很久以后才使用,这样就会造成内存空间白白被占用。

这种情况下可以加上lazy关键字,延后变量/常量赋值的位置。这样直到后续真正用到这个量时才真正开辟空间赋值,减少了内存的浪费。

val name = "zhang"//直接分配空间 即使一时半会用不到

lazy val name = "zhang"//并不会立即分配空间 直到后续用到了才会分配

示例1

object Demo01 {

 val val1="zhang"                                //> val1  : String = zhang

 //lazy

 lazy val val2="zhang"                           //> val2: => String

}

四、caseclass——样例类

1. case class - 样例类

1. 只要在声明类时 在class关键字前加上case关键字 这个类就成为了样例类

样例类必须要显式的声明一个主构造器

2. 当样例类声明一个主构造器后,会默认隐式的声明一个空构造器

3. 样例类默认实现序列化接口

4. 样例类默认自动覆盖 toString 方法

5. 样例类不需要new可以直接生成对象

示例:

//需要声明一个主构造器,主构造器可以是一个空构造器

case class Item(var1:String,var2:Int){

  var name=var1;

  var age=var2;

}

object Demo03 {

  def main(args: Array[String]): Unit = {

    //样例类不需要new,并且隐式的含有空构造器

    val i1=Item

    val i2=Item("tom",23)

    println(i2.name+i2.age)

  }

}

五、option类型

1. option

在Scala中用来表示一个结果,它可能有值,也可能没有值,它有两个子Option。

示例2

object Demo02 {

  def f1(a:Int,b:Int):Option[Int]={

    if(b!=0){

      Some(a/b)

    }else{

      None

    }

  }

  def main(args: Array[String]): Unit = {

    //表示如果有正确结果,返回正确结果,没有则返回指定的默认值

     val Result=f1(4,2).getOrElse(0)

  }

}

六、scala隐式转换机制

1. 概述

scala implicit关键字详解(隐式转换函数、隐式类、隐式参数、隐式值),implicit是scala中的一个关键字,关于它有着丰富的用法,使得scala更灵活和容易扩展。

2. 隐式转换函数

implicit def int2str(x:Int):String = x.toString

这段代码声明了一个函数int2str,它与正常函数唯一的区别在于前面多出的implicit关键字。这里的implicit就是它字面的含义——隐式,它告诉编译器,这个函数是一个隐式转换函数,能够把Int类型的值转换成String类型的值。

这种隐式转换的意义在于,如果在进行一个对Int类型的操作时不合法,编译器会在当前作用域寻找合适的隐式转换,来尝试使这种操作合法。

需要注意:

1.对于隐式转换函数,编译器最关心的是它的类型签名,即它将哪一种类型转换到另一种类型,也就是说它应该接受只一个参数,对于接受多参数的隐式函数来说就没有隐式转换的功能了。

implicit def int2str(x:Int):String = x.toString // 正确

implicit def int2str(x:Int,y:Int):String = x.toString // 错误

2.不能存在二义性,即同一个作用域不能定义两个相同类型的隐式转换函数,这样编译器将无法决定使用哪个转换。

implicit def int2str(x:Int):String = x.toString

implicit def anotherInt2str(x:Int):A = x.toString

3. 隐式类

scala也支持隐式类的声明,在隐式类中定义的方法都是隐式转换方法。如下:

//定义一个隐式类,用于扩展String类型的操作方法(提取后缀)

implicit class AuthMethod(x:String){

          def suffix()={

                  x.split("\\.").last

          }

  }

  //提取文件后缀

  v4.suffix()   //> res0: String = txt

4. 隐式参数

在当前作用域里存在一个Adder[Int]类型的隐式值implicit val a。

在调用f2时,编译器可以找到implicit标记过的a,所以我们不必传递隐式参数而是直接调用f2(2,3)。而如果你想要传递隐式参数的话,你也可以自定义一个传给它。

trait Adder[T]{

          def add(x:T,y:T):T

  }

implicit val a=new Adder[Int]{

  override def add(x:Int,y:Int)=x+y

  }    

def f2(x:Int,y:Int)(implicit adder:Adder[Int])={

          adder.add(x, y)

 }  

f2(2,3)(new Adder[Int]{

          override def add(x:Int,y:Int)=x+y

 }) 

f2(2,3)(a) 

f2(2,3) 

七、Scala第三天练习作业

1.对"Hello"和"World"进行拉链操作,会产生什么结果?

2.编写函数values(fun:(Int)=>Int,low:Int,high:Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。比如:

-5,-4,-3,-2,-1,0,1,2,3,4,5

values(x=>x*x,-5,5)应该产出一个对偶的集合(-5,25),(-4,16),(-3,9),…,(3,9),(5,25)

3.编写函数largest(fun:(Int)=>Int,inputs:Seq[Int]),输出在给定输入序列中给定函数的最大值。举例来说,

largest(x=>10*x-x*x,1 to 10)应该返回25

1=>x=>10*x-x*x=9

2=>x=>10*x-x*x=16

……

返回其中的最大值 25

4.修改前一个函数,返回最大的输出对应的输入。举例来说,largestAt(fun:(Int)=>Int,inputs:Seq[Int])应该返回5。

5.编写一个函数,接收一个整数数组(Array[Int]),产出一个新的数组。

包含原数组中的所有正值,以原有顺序排列,之后的元素是所有零或负值,以原有顺序排列。

比如: Array(4, -2, 0, -3, 0, 1, 5) ,处理后的结果是:Array4 ,1, 5, 0, 0, -2, -3

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值