scala学习 之 及 基本和高级用法(二)

本文全面介绍了Scala编程语言的基础知识,涵盖数据类型、变量、方法、集合、类与对象、特质、模式匹配等内容,深入浅出地讲解了Scala的核心概念与实用技巧。

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

目录
1. Scala的数据类型
2. Scala变量
3. Scala 访问修饰符
4. Scala 方法和函数
5. Scala数组
6. Collection(集合)
7. Scala的类和对象
8. Scala的接口trait
9. Scala的模式匹配
10. Scala的正则匹配
11. 异常处理
12. 提取器
13. Scala的文件I/O
14. 偏函数


1. Scala的数据类型

比Java多出的数据类型有:

Unit: 表示无值,和其他语言中void等同。用作不返回任何结果的方法的结果类型。Unit只有一个实例值,写成()。
Nothing Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。

Nothing :Nothing类型在Scala的类层级的最低端;它是任何其他类型的子类型。

Any :Any是所有其他类的超类

AnyRef :AnyRef类是Scala里所有引用类(reference class)的基类

多行字符串的表示方法:

多行字符串用三个双引号来表示分隔符,格式为:”“” … “”“。

2. Scala变量

在 Scala 中,使用关键词 “var” 声明变量,使用关键词 “val” 声明常量。

var myVar : String = "Foo"
var myVar : String = "Too"

在 Scala 中声明变量和常量不一定要指明数据类型,在没有指明数据类型的情况下,其数据类型是通过变量或常量的初始值推断出来的。

var myVar = 10;
val myVal = "Hello, Scala!";

3. Scala 访问修饰符

Scala 访问修饰符有:private,protected,public。

如果没有指定访问修饰符符,默认情况下,Scala 对象的访问级别都是 public。

Scala 中的 private 限定符,比 Java 更严格,在嵌套类情况下,外层类甚至不能访问被嵌套类的私有成员。

在 scala 中,对保护(Protected)成员的访问比 java 更严格一些。因为它只允许保护成员在定义了该成员的的类的子类中被访问。而在java中,用protected关键字修饰的成员,除了定义了该成员的类的子类可以访问,同一个包里的其他类也可以进行访问。

作用域保护:

private[x]
protected[x]

这里的x指代某个所属的包、类或单例对象。如果写成private[x],读作”这个成员除了对[…]中的类或[…]中的包中的类及它们的伴生对像可见外,对其它所有类都是private。

4. Scala 方法和函数

Scala 有方法与函数,二者在语义上的区别很小。Scala 中的方法跟 Java 的类似,方法是组成类的一部分。Scala 中的函数则是一个完整的对象,Scala 中的函数其实就是继承了 Trait 的类的对象。
Scala 中使用 val 语句可以定义函数,def 语句定义方法。

class Test{
  def m(x: Int) = x + 3
  val f = (x: Int) => x + 3
}
方法的定义:
def functionName ([参数列表]) : [return type] = {
   function body
   return [expr]
}

object add{
   def addInt( a:Int, b:Int ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}
Scala 函数传名调用(call-by-name)

Scala的解释器在解析函数参数(function arguments)时有两种方式:

  • 传值调用(call-by-value):先计算参数表达式的值,再应用到函数内部;

  • 传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部

在进入函数内部前,传值调用方式就已经将参数表达式的值计算完毕,而传名调用是在函数内部进行参数表达式的值计算的。这就造成了一种现象,每次使用传名调用时,解释器都会计算一次表达式的值。

object Test {
   def main(args: Array[String]) {
        delayed(time());
   }

   def time() = {
      println("获取时间,单位为纳秒")
      System.nanoTime
   }
   def delayed( t: => Long ) = {
      println("在 delayed 方法内")
      println("参数: " + t)
      t
   }
}
在 delayed 方法内
获取时间,单位为纳秒
参数: 241550840475831
获取时间,单位为纳秒
指定函数参数名
object Test {
   def main(args: Array[String]) {
        printInt(b=5, a=7);
   }
   def printInt( a:Int, b:Int ) = {
      println("Value of a : " + a );
      println("Value of b : " + b );
   }
}
Value of a :  7
Value of b :  5
默认参数值
object Test {
   def main(args: Array[String]) {
        println( "返回值 : " + addInt() );
   }
   def addInt( a:Int=5, b:Int=7 ) : Int = {
      var sum:Int = 0
      sum = a + b

      return sum
   }
}
$ scalac Test.scala
$ scala Test
返回值 : 12
可变参数
object Test {
   def main(args: Array[String]) {
        printStrings("Runoob", "Scala", "Python");
   }
   def printStrings( args:String* ) = {
      var i : Int = 0;
      for( arg <- args ){
         println("Arg value[" + i + "] = " + arg );
         i = i + 1;
      }
   }
}
Arg value[0] = Runoob
Arg value[1] = Scala
Arg value[2] = Python
Scale函数嵌套
object Test {
   def main(args: Array[String]) {
      println( factorial(0) )
      println( factorial(1) )
      println( factorial(2) )
      println( factorial(3) )
   }

   def factorial(i: Int): Int = {
      def fact(i: Int, accumulator: Int): Int = {
         if (i <= 1)
            accumulator
         else
            fact(i - 1, i * accumulator)
      }
      fact(i, 1)
   }
}
1
1
2
6
偏应用函数
import java.util.Date

object Test {
   def main(args: Array[String]) {
      val date = new Date
      log(date, "message1" )
      Thread.sleep(1000)
      log(date, "message2" )
      Thread.sleep(1000)
      log(date, "message3" )
   }

   def log(date: Date, message: String)  = {
     println(date + "----" + message)
   }
}

我们使用偏应用函数优化以上方法,绑定第一个 date 参数,第二个参数使用下划线(_)替换缺失的参数列表

import java.util.Date

object Test {
   def main(args: Array[String]) {
      val date = new Date
      val logWithDateBound = log(date, _ : String)

      logWithDateBound("message1" )
      Thread.sleep(1000)
      logWithDateBound("message2" )
      Thread.sleep(1000)
      logWithDateBound("message3" )
   }

   def log(date: Date, message: String)  = {
     println(date + "----" + message)
   }
}
高阶函数

高阶函数(Higher-Order Function)就是操作其他函数的函数。

object Test {
   def main(args: Array[String]) {

      println( apply( layout, 10) )

   }
   // 函数 f 和 值 v 作为参数,而函数 f 又调用了参数 v
   def apply(f: Int => String, v: Int) = f(v)

   def layout[A](x: A) = "[" + x.toString() + "]"

}
[10]
匿名函数

匿名函数箭头左边是参数列表,右边是函数体。

var inc = (x:Int) => x+1

inc 现在可作为一个函数,使用方式如下

var x = inc(7)-1

我们也可以不给匿名函数设置参数,如下所示:

var userDir = () => { System.getProperty("user.dir") }
函数柯里化(Currying)

柯里化(Currying)指的是将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数为参数的函数。

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

//柯里化
def add(x:Int)(y:Int) = x + y

5. Scala数组

初始化方式:
var z:Array[String] = new Array[String](3)
var z = new Array[String](3)
var z = Array("Runoob", "Baidu", "Google")
遍历数组
object Test {
   def main(args: Array[String]) {
      var myList = Array(1.9, 2.9, 3.4, 3.5)

      // 输出所有数组元素,x代表值
      for ( x <- myList ) {
         println( x )
      }

      // 计算数组所有元素的总和,i代表索引
      var total = 0.0;
      for ( i <- 0 to (myList.length - 1)) {
         total += myList(i);
      }
      println("总和为 " + total);
   }
}
合并数组
object Test {
   def main(args: Array[String]) {
      var myList1 = Array(1.9, 2.9, 3.4, 3.5)
      var myList2 = Array(8.9, 7.9, 0.4, 1.5)

      var myList3 =  concat( myList1, myList2)
   }
}
创建区间数组
//range() 方法最后一个参数为步长,默认为 1

import Array._

object Test {
   def main(args: Array[String]) {
      var myList1 = range(10, 20, 2)
      var myList2 = range(10,20)

      // 输出所有数组元素
      for ( x <- myList1 ) {
         print( " " + x )
      }
      println()
      for ( x <- myList2 ) {
         print( " " + x )
      }
   }
}

6. Collection(集合)

// 定义整型 List
val x = List(1,2,3,4)

// 定义 Set
val x = Set(1,3,5,7)

// 定义 Map
val x = Map("one" -> 1, "two" -> 2, "three" -> 3)

// 创建两个不同类型元素的元组
val x = (10, "Runoob")

// 定义 Option
val x:Option[Int] = Some(5)
List的操作

在官方文档中List是immutable的,所以对List的所谓增上改查都是产生新的对象,并非在原来的集合上改动

遍历(跟数组的遍历方法一样)

  val  a = List(1,2,3,4)
  //for (i <- a) println(i)
  //for (i <- 0 to a.length-1) println(a(i))

  a.foreach { x => print(x +" ")} 
  println(a.mkString(" "))  //mkString方法适合输出

对单个元素的操作

var list = List("scala","spark","hadoop") 
"test"::list // 添加到元素首对象 (返回新对象,源对象不变) 
list ::= "test"

list(0)      //取  
list apply 0
list.last //最后一个
list.tail //

list = list.updated(2,"hadoop1") // 返回新对象,源对象不变

lsit = list.drop(3);//从左边丢掉3个元素
lsit = list.dropRight(3)

对集合的基本操作

++  :[B](that: GenTraversableOnce[B]): List[B] 从列表的尾部添加另外一个列表
++:  :[B >: A, That](that: collection.Traversable[B])(implicit bf: CanBuildFrom[List[A], B, That]): That 在列表的头部添加一个列表

+:  :(elem: A): List[A] 在列表的头部添加一个元素
:+  :(elem: A): List[A] 在列表的尾部添加一个元素

对集合的惊人操作

求一个List的最大值

val num = List(11,2,5,1,6)
println(num.max);

//根据书的页数排序
class  Book(title:String, pages:Int)
val  books =
Seq(
  Book("Future of Scala developers", 85),
  Book("Parallel algorithms", 240),
  Book("Object Oriented Programming", 130),
  Book("Mobile Development", 495)
)
books.maxBy(book => book.pages)

过滤集合元素

val numbers = Seq(1,2,3,4,5,6,7,8,9,10)
numbers.filter(n => n % 2  ==  0)

//获取页数大于120页的书
class  Book(title:String, pages:Int)
val  books =
Seq(
  Book("Future of Scala developers", 85),
  Book("Parallel algorithms", 240),
  Book("Object Oriented Programming", 130),
  Book("Mobile Development", 495)
)
books.filter(book => book.pages >= 120)

filterNot方法与filter方法相反

展开嵌套结构

scala> List(List(1, 2), List(3, 4)).flatten
res0: List[Int] = List(1, 2, 3, 4)

欧拉图函数

欧拉图函数就是差集、交集和并集

val num1 = Seq(1, 2, 3, 4, 5, 6)
val num2 = Seq(4, 5, 6, 7, 8, 9)

//List(1, 2, 3)
num1.diff(num2)

//List(4, 5, 6)
num1.intersect(num2)

//List(1, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8, 9)
num1.union(num2)

//List(1, 2, 3, 4, 5, 6, 7, 8, 9)
num1.union(num2).distinct

map函数

map函数遍历集合中的元素并对每个元素调用函数

val numbers = Seq(1,2,3,4,5,6)

//List(2, 4, 6, 8, 10, 12)
numbers.map(n => n * 2)

val chars = Seq('a', 'b', 'c', 'd')

//List(A, B, C, D)
chars.map(ch => ch.toUpper)

flatMap函数

flatMap 是由下列这两个函数组成的:map & flatten

val abcd = Seq('a', 'b', 'c', 'd')

//List(A, a, B, b, C, c, D, d)
abcd.flatMap(ch => List(ch.toUpper, ch))

对整个集合进行条件检查

集合的元素有哪怕一个元素不符合条件,就返回false

val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)

//ture
numbers.forall(n => n < 10)

//false
numbers.forall(n => n > 5)

集合分组

val numbers = Seq(3, 7, 2, 9, 6, 5, 1, 4, 2)

//(List(2, 6, 4, 2), List(3, 7, 9, 5, 1))
(part1,part2) = numbers.partition(n => n % 2  ==  0)

fold和reduce

reduce就是以第一个元素为初始值,依次对后面的元素进行操作
fold就是在第一对括号中,放一个起始值,在第二对括号中,我们定义需要对数字序列的每个元素执行的操作。

//从集合的左边两个元素开始,得到的结果跟下一个元素继续操作
var temp1 = lst.reduceLeft((sum,i) => sum +i) //求和
var temp1 = lst.reduceLeft(_+_) //简写

//reduceLeft只是简单的把list的第一个元素作为运算的开始元素,而foldLeft则需要传入一个元素作为开始元素。
var temp1 = lst.foldLeft(0)((sum,i) => sum +i)
var temp1 = lst.foldLeft(0)(_+_) 
println(a./:(0)(_+_)) //  /:方法等价于foldLeft

排序

object Test {
  def main(args: Array[String]) {
    val p = List(2,5,23,3,4);
    var s = p.sortWith(_<_)
    println(s.mkString(" ")) //23 5 4 3 2
  }
}

7. Scala的类和对象

我们可以使用 new 关键字来创建类的对象,实例如下:

class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点: " + x);
      println ("y 的坐标点: " + y);
   }
}

类的继承

  • 重写一个非抽象方法必须使用override修饰符。
  • 只有主构造函数才可以往基类的构造函数里写参数。
  • 在子类中重写超类的抽象方法时,你不需要使用override关键字。
class Point(xc: Int, yc: Int) {
   var x: Int = xc
   var y: Int = yc

   def move(dx: Int, dy: Int) {
      x = x + dx
      y = y + dy
      println ("x 的坐标点: " + x);
      println ("y 的坐标点: " + y);
   }
}

class Location(override val xc: Int, override val yc: Int,
   val zc :Int) extends Point(xc, yc){
   var z: Int = zc

   def move(dx: Int, dy: Int, dz: Int) {
      x = x + dx
      y = y + dy
      z = z + dz
      println ("x 的坐标点 : " + x);
      println ("y 的坐标点 : " + y);
      println ("z 的坐标点 : " + z);
   }
}

scala的单例模式

在Scala中,是没有static这个东西的,但是它也为我们提供了单例模式的实现方法,那就是 object,
Scala中使用单例模式时,除了定义的类之外,还要定义一个同名的object对象,它和类的区别是,object对象不能带参数。

/**这是我们定义的类
构造函数被我们定义为private的,防止直接调用该类的构造来创建对象
*/
class StaticTest private {
  private def add_(x: Int, y: Int): Int = {
    return x + y
  }

  private def sub_(x: Int, y: Int): Int = {
    return x + y
  }
}

/**这个就是单例模式的定义,和类同名,且不带参数*/
object StaticTest{
//内部声明一个StaticTest类实例对象
  val singleObj = new StaticTest
  //applay方法,当执行 StaticTest() 时被执行
  def apply()={
    println("-------apply--------")
  }
  def add(x:Int,y:Int):Int={
  //调用StaticTest 类的方法
    return singleObj.add_(x,y)
  }
  //调用StaticTest 类的方法
  def sub(x:Int,y:Int):Int ={
    return singleObj.sub_(x,y)
  }
}

使用

//定义一个单例对象
val test = StaticTest
//调用add方法
println(test.add(2,3))

8. Scala的接口trait

Scala Trait(特征) 相当于 Java 的接口,与接口不同的是,它还可以定义属性和方法的实现。

类继承Trait后,必须实现其中的抽象方法,实现时不需要使用override关键字,同时Scala同Java一样,不支持类多继承,但支持多重继承Trait,使用with关键字即可。

trait Equal {
  //没有定义方法的实现
  def isEqual(x: Any): Boolean

  //定义了方法的实现
  def isNotEqual(x: Any): Boolean = !isEqual(x)
}

class Point(xc: Int, yc: Int) extends Equal {
  var x: Int = xc
  var y: Int = yc
  def isEqual(obj: Any) =
    obj.isInstanceOf[Point] &&
    obj.asInstanceOf[Point].x == x
}
Trait高级
  1. 混合使用Trait的具体方法和抽象:可以让具体方法依赖于抽象方法,抽象方法放到继承Trait的类中实现,这是模板方法常用的方法。
trait MakeBread{
  def before() {println("做准备")}
  def make()
  def after() {println("完成了")}
  def makeBread() {
     before()
     make()
     after()
  }
}

class Sandwich extends MakeBread{
   def make(){
     println("做三明治")
   }
}

object Test{
  def main(args : Array[String]){
    var bread = new Sandwich
    bread.makeBread();
  }
}

结果:

做准备
做三明治
完成了
  1. Trait构造机制:在Scala中,Trait是有构造代码的,就是Trait中不包含在任何方法中的代码,而继承了Trait的构造机制如下:
    • 父类的构造函数
    • Trait的构造代码执行,多个Trait从左向右依次执行
    • 构造Trait时会先构造父Trait,如果多个Trait继承同一个父Trait,则父Trait只会构造一次
    • 所有trait构造结束之后,子类的构造函数执行
class People { println("this is People's class Constructor")}
trait cloth{println("this is cloth's trail Constructor")}
trait Pant{println("this is pant's trail Constructor")}

class Man extends People with cloth with Pant{
   println("this is Man's class Constructor")
}

object Test{
  def main(args : Array[String]){
    var bread = new Man
  }
}

结果:

this is People's class Constructor
this is cloth's trail Constructor
this is pant's trail Constructor
this is Man's class Constructor

9. Scala的模式匹配

match 对应 Java 里的 switch,但是写在选择器表达式之后。即: 选择器 match {备选项}。

def matchTest(x: Any): Any = x match {
      case 1 => "one"
      case "two" => 2
      case y: Int => "scala.Int"
      case _ => "many"
   }

比较对象

object Test {
   def main(args: Array[String]) {
       val alice = new Person("Alice", 25)
    val bob = new Person("Bob", 32)
       val charlie = new Person("Charlie", 32)

    for (person <- List(alice, bob, charlie)) {
        person match {
            case Person("Alice", 25) => println("Hi Alice!")
            case Person("Bob", 32) => println("Hi Bob!")
            case Person(name, age) =>
               println("Age: " + age + " year, name: " + name + "?")
         }
      }
   }
   // 样例类
   case class Person(name: String, age: Int)
}

处理异常

def processException(e: Exception) {
  e match {
    case e1: IllegalArgumentException => println("you have illegal arguments! exception is: " + e1)
    case e2: FileNotFoundException => println("cannot find the file you need read or write!, exception is: " + e2)
    case e3: IOException => println("you got an error while you were doing IO operation! exception is: " + e3)
    case _: Exception => println("cannot know which exception you have!" )
  }
}

Scala有一种特殊的类型,叫做Option。Option有两种值,一种是Some,表示有值,一种是None,表示没有值。

Option通常会用于模式匹配中,用于判断某个变量是有值还是没有值,这比null来的更加简洁明了

Option的用法必须掌握,因为Spark源码中大量地使用了Option,比如Some(a)、None这种语法,因此必须看得懂Option模式匹配,才能够读懂spark源码。

// 案例:成绩查询
val grades = Map("Leo" -> "A", "Jack" -> "B", "Jen" -> "C")

def getGrade(name: String) {
  val grade = grades.get(name)
  grade match {
    case Some(grade) => println("your grade is " + grade)
    case None => println("Sorry, your grade information is not in the system")
  }
}

10. Scala的正则匹配

完全匹配

val pattern = "^[a-z0-9._%\\-+]+@(?:[a-z0-9\\-]+\\.)+[a-z]{2,4}$"
val emailRegex = pattern.r // r()方法将字符串转化成Regex对象
emailRegex.pattern.matcher("tt@16.cn").matches // true

查找匹配部分

object Test {
  def main(args: Array[String]) {
    val p = "[0-9]+".r
    var s = p.findAllIn("2 ad 12ab ab21 23").toList // List(2, 12, 21, 23)
    var k = p.findFirstMatchIn("abc123xyz") // scala.util.matching.Regex.Match = 123
    println(s.mkString(" "))   //2 12 21 23
    println(k)                 //Some(123)
    println(k.get)             //123
  }
}

11. 异常处理

捕捉异常的catch子句,语法与其他语言中不太一样。在Scala里,借用了模式匹配的思想来做异常的匹配,因此,在catch的代码里,是一系列case字句,如下例所示:

object Test {
   def main(args: Array[String]) {
      try {
         val f = new FileReader("input.txt")
      } catch {
         case ex: FileNotFoundException =>{
            println("Missing file exception")
         }
         case ex: IOException => {
            println("IO Exception")
         }
      }
   }
}

异常的其他特征跟Java差不多,这里就不在介绍。

12. 提取器

apply方法我们已经非常熟悉了,它帮助我们无需new操作就可以创建对象,而unapply方法则用于析构出对象,在模式匹配中特别提到,如果一个类要能够应用于模式匹配当中,必须将类声明为case class,因为一旦被定义为case class,scala会自动帮我们生成相应的方法,这些方法中就包括apply方法及unapply方法。

object EMail{

  //apply方法用于无new构造对象
  def apply(user: String, domain: String) = user + "@" + domain

  //unapply方法用于在模式匹配中充当extractor
  def unapply(str: String): Option[(String, String)] = {
    val parts = str split "@"
    if (parts.length == 2) Some(parts(0), parts(1)) else None
  }
}
object ApplyAndUnapply {
  val email=EMail("134214214","qq.com")

  def patternMatching(x:String)=x match {

    //下面的匹配会导致调用EMail.unapply(email)
    case EMail(user,domain) => println("user="+user+" domain="+domain)
    //匹配非法邮箱
    case _ => println("non illegal email")
  }
}

13. Scala的文件I/O

从屏幕上读取用户输入:

import scala.io._
object Test {
   def main(args: Array[String]) {
      print("请输入菜鸟教程官网 : " )
      val line = StdIn.readLine()

      println("谢谢,你输入的是: " + line)
   }
}

从文件上读写内容:

import java.io._
import scala.io.Source

object Test {
   def main(args: Array[String]) {
      val writer = new PrintWriter(new File("test.txt" ))
      writer.write("写内容")
      writer.close()

      Source.fromFile("test.txt" ).foreach{ 
         print 
      }
   }
}

14. 偏函数

Scala中的Partia Function是一个Trait,其的类型为PartialFunction[A,B],其中接收一个类型为A的参数,返回一个类型为B的结果。

scala> val pf:PartialFunction[Int,String] = {     case 1=>"One" 
    case 2=>"Two" 
    case 3=>"Three" 
    case _=>"Other" 

} 

pf: PartialFunction[Int,String] = <function1> 

scala> pf(1) 
res0: String = One 

scala> pf(2) 
res1: String = Two 

scala> pf(3) 
res2: String = Three 

scala> pf(4) 
res3: String = Other

isDefinedAt,这个函数的作用是判断传入来的参数是否在这个偏函数所处理的范围内。

 scala> pf.isDefinedAt(1) 
 res4: Boolean = true 

 scala> pf.isDefinedAt(2) 
 res5: Boolean = true 

 scala> pf.isDefinedAt("1") 

 <console>:13: error: type mismatch; found : String("1") required: Int pf.isDefinedAt("1") ^ 

 scala> pf.isDefinedAt(100) 
 res7: Boolean = true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值