Scala 重难点总结

目录

  • [一. 难点总结](#一. 难点总结)
    • [1. 数据类型](#1. 数据类型)
    • [2. 函数与方法的区别](#2. 函数与方法的区别)
    • [3. object、class、case class、case object、package object](#3. object、class、case class、case object、package object)
    • [4. 伴生对象和 apply() 到底有什么用](#4. 伴生对象和 apply() 到底有什么用)
    • [5. 模式匹配 unapply(),case class 和 偏函数](#5. 模式匹配 unapply(),case class 和 偏函数)
    • [6. 隐式转换](#6. 隐式转换)
    • [7. 泛型](#7. 泛型)
  • 二.参考文章

一. 难点总结

1. 数据类型

scala 复制代码
def test1():Any = {List}

// 类似于 Java 的 Object
def test2():AnyRef = {List}

def test3():AnyVal = {1}

Unit、Null、Nothing、Nil、None 的区别:

scala 复制代码
// AnyVal 类型的子类
def test4():Unit = {}

// AnyRef 类型的子类
def test5():Null= {null}

// Nothing 表示抛出异常 或者 集合为空,是最底层的子类
def test6():Int = {if(1 < 0) 1 else throw new Exception }  // 实际会返回 Nothing

def test7():List[Nothing] = {List()}

def test8():List[Nothing] = {Nil}

// None 是对象 不是类,它的类型是 Option[Nothing]
def test9():Option[String] = { Map("France"->"Paris").get("a") }  //返回 Some(Paris) 或者 None 对象

2. 函数与方法的区别

首先了解下函数与方法的区别:Scala 方法是类的一部分,而 函数 是一个对象可以赋值给一个变量。方法本质上是一个特殊的函数。

java 复制代码
scala> val a = 1                           //数据类型赋值给变量
a: Int = 1

scala> val f1 = (x:Int,y:Int) => x + y     //函数赋值给变量
f1: (Int, Int) => Int = <function>         //<function>就是 x + y 这个函数体,是一个 lambda 表达式

scala> def m(x: Int) = x + 3              //定义方法
m: (x: Int)Int

scala> val f2 = m _                       //方法转换为函数
f2: Int => Int = <function2>

Scala 中的函数则是一个完整的对象。方法中可以传函数,对象构造器也可以传函数。map 函数也称为高阶函数。

scala 复制代码
def map[U: ClassTag](f: T => U): RDD[U] = withScope {  //方法的形参为一个函数
  val cleanF = sc.clean(f)
  new MapPartitionsRDD[U, T](this, (_, _, iter) => iter.map(cleanF)) //主构造器
}
scala 复制代码
class Test{
	def m(x: Int) = x + 3                    //方法
	val f = (x: Int) => x + 3                //变量

	val f2 = (x: Int, y: Int) => x + y       //变量
	def m1(f:(Int,Int) => Int) : Int = {     //方法的形参为一个函数
		f(2,6)
	}

	print(m1(f2))   // 输出 8
}

3. object、class、case class、case object、package object

  1. object:相当于单例对象,可以直接使用。
  2. class:类定义
  3. case class:
    • 是用于创建不可变(immutable)且具有模式匹配能力的类。
    • 重写了 toString、equals、hashCode 方法。
    • 伴生对象中提供了apply方法,所以可以不使用new关键字就可构建对象。
    • 提供 unapply 方法使模式匹配可以工作。
  4. case object:既是一个单例对象,又具有模式匹配的能力。
  5. package object:包下的单例对象,对象名与包名相同。

4. 伴生对象和 apply() 到底有什么用

伴生对象 => 单例对象,当做工具类使用,尽量来操作这个对象。

延申知识点:java 中有 static 和 单例。

scala 复制代码
val a = Array(1,2,3)  
// 使用的是 Array 的这个伴生对象,伴生对象() => 就是调用对象中的 apply() 方法。

apply() => 语法糖,val a = Array(1,2,3) 底层 new 的细节被屏蔽了。

scala 复制代码
object ApplyApp {
  def main(args: Array[String]): Unit = {
    for(i<- 1 to 10){
      ApplyTest.incr
    }
    println(ApplyTest.count)  //结果为10,说明object本身就是一个单例对象

    val b=ApplyTest()    //因为在object的apply已经new了,所以此处就不用了new了
    //此时调用Object ApplyTest里面的apply方法
    
    val c=new ApplyTest()
    println(c)              //此处c默认使用toString方法
    c()                     //此处调用class 里面的apply方法
    
     /* 即类名()=->object里面的apply方法:val b=ApplyTest()
     *   对象()==>class里面的apply方法:val c=new ApplyTest()
     */
  }
}
scala 复制代码
//class ApplyTest是object ApplyTest的伴生类
class ApplyTest{
  def apply() ={
    println("class ApplyTest里面的apply方法被调用")
  }
}

//object ApplyTest是class ApplyTest的伴生对象
object ApplyTest{
  println("伴生对象进入")
  var count=0
  def incr={
    count=count+1
  }
//一般情况下,我们在object里面的apply方法中去new class
  def apply() ={
    println("object ApplyTest里面的apply方法被调用")
    new ApplyTest
  }
  println("伴生对象离开")
}

5. 模式匹配 unapply(),case class 和 偏函数

unapply 是接受一个对象,从对象中提取出相应的值,主要用于模式匹配中。

scala 复制代码
class Money(val value: Double, val country: String) {}
object Money {
    def apply(value: Double, country: String) : Money = new Money(value, country)

    def unapply(money: Money): Option[(Double, String)] = {
        if(money == null) {
            None
        } else {
            Some(money.value, money.country)
        }
    }
}

val money = Money(10.1, "RMB")
money match {
    case Money(num, "RMB") =>  println("RMB: " + num)
    case _ => println("Not RMB!")
}

直接使用 case class,不用自己实现 unapply 方法

scala 复制代码
val person = new Person("Alice", 25)

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)

偏函数:就是用花括号包围一些 case 语句。

scala 复制代码
object PartialFunctionExample extends App {
  val multiplyByTwo: PartialFunction[Int, Int] = {
    case x => x * 2
  }
 
  println(multiplyByTwo(2)) // 输出: 4
}

6. 隐式转换

主要作用一:模拟新的语法

  • select中的 $ 是隐式转换
java 复制代码
import spark.implicits._
df.select($"name", $"age" + 1).show()

implicit class StringToColumn(val sc: StringContext) {
  def $(args: Any*): ColumnName = {
    new ColumnName(sc.s(args: _*))
  }
}
  • map中的 -> 是隐式转换
java 复制代码
val map = Map(1 -> "one")

implicit final class ArrowAssoc[A](private val self: A) extends AnyVal {
  @inline def -> [B](y: B): Tuple2[A, B] = Tuple2(self, y)
  def →[B](y: B): Tuple2[A, B] = ->(y)
}

主要作用二:类型增强与扩展

  • rdd 的 api 并没有 toDF() 方法,如果要使用必须的隐式转化
java 复制代码
import spark.implicits._

val peopleRDD: RDD[String] = spark.sparkContext.textFile("D:\\study\\people.txt")
val peopleDF: DataFrame = peopleRDD.map(_.split(",")) //RDD
  .map(x => People(x(0), x(1).trim.toInt)) //RDD
  .toDF()
  
implicit def rddToDatasetHolder[T : Encoder](rdd: RDD[T]): DatasetHolder[T] = {
  DatasetHolder(_sqlContext.createDataset(rdd))
}

Spark 中的 RDD 以及它的子类是没有 groupByKey, reduceByKey 以及 join 这一类基于 key-value 元组的操作的,但是在你使用 RDD 时,这些操作是实实在在存在的,Spark 正是通过隐式转换将一个 RDD 转换成了 PairRDDFunctions

java 复制代码
implicit def rddToPairRDDFunctions[K, V](rdd: RDD[(K, V)])
  (implicit kt: ClassTag[K], vt: ClassTag[V], ord: Ordering[K] = null): PairRDDFunctions[K, V] = {
  new PairRDDFunctions(rdd)
}

7. 泛型

类泛型

  1. java泛型
java 复制代码
public class People<T>{
    //成员变量
    private T t;
}
  1. scala 主构造器泛型
scala 复制代码
class People[T](val t: T)
  1. 视图界定
scala 复制代码
class MaxValue[T <: Comparable[T]](val first:T, val second:T){ //泛型
  def bigger = if(first.compareTo(second) > 0) first else second
}

print(new MaxValue[Integer](1, 2).bigger)   

Integer 实现了 Comparable[Integer] 接口,能正常运行。

  1. 泛型和隐式转换
scala 复制代码
class MaxValue[T <: Comparable[T]](val first:T, val second:T){ //泛型
  def bigger = if(first.compareTo(second) > 0) first else second
}

print(new MaxValue[Int](1, 2).bigger)   

Int 没实现了 Comparable[Int] 接口,不能能正常运行。

将 T <: Comparable[T] 改为 T <% Comparable[T],会隐式转换将 Int 转为 Integer,就能正常运行。

二.参考文章

Scala 方法与函数
Scala中的下划线使用总结
为什么Scala在类中没有静态成员?
java中的静态类
秒懂设计模式之建造者模式
scala中的apply方法与unapply方法
【Scala】使用Option、Some、None,避免使用null
scala 里怎样判空,比如java中防止空指针异常而做出的Null判断,以及字符串blank判断等?
Scala之隐式转换
SparkSql学习之:toDF方法的由来(源码)

相关推荐
虞书欣的61 分钟前
Python小游戏24——小恐龙躲避游戏
开发语言·python·游戏·小程序·pygame
源码12156 分钟前
ASP.NET MVC宠物商城系统
后端·asp.net·宠物
FHYAAAX9 分钟前
【机器学习】任务十:从函数分析到机器学习应用与BP神经网络
开发语言·python
汉克老师27 分钟前
GESP4级考试语法知识(贪心算法(四))
开发语言·c++·算法·贪心算法·图论·1024程序员节
爱吃生蚝的于勒1 小时前
C语言最简单的扫雷实现(解析加原码)
c语言·开发语言·学习·计算机网络·算法·游戏程序·关卡设计
Ai 编码助手1 小时前
Go语言 实现将中文转化为拼音
开发语言·后端·golang
hummhumm1 小时前
第 12 章 - Go语言 方法
java·开发语言·javascript·后端·python·sql·golang
hummhumm1 小时前
第 8 章 - Go语言 数组与切片
java·开发语言·javascript·python·sql·golang·database
何曾参静谧1 小时前
「QT」文件类 之 QDir 目录类
开发语言·qt
何曾参静谧1 小时前
「QT」文件类 之 QTemporaryDir 临时目录类
开发语言·qt