【大数据】初识Scala 4

本文涉及的产品
云原生大数据计算服务MaxCompute,500CU*H 100GB 3个月
云原生大数据计算服务 MaxCompute,5000CU*H 100GB 3个月
简介: 【大数据】初识Scala

类型限界

在 Scala 中,类型上界(Upper Bounds)和类型下界(Lower Bounds)是用于限制泛型类型参数的范围的概念。它们允许我们在泛型类或泛型函数中指定类型参数必须满足某种条件。下面是关于类型上界和类型下界的解释和示例代码:

类型上界

类型上界(Upper Bounds): 类型上界用于指定泛型类型参数必须是某个类型或其子类型。我们使用 <: 符号来定义类型上界。例如,A <: B 表示类型参数 A 必须是类型 B 或其子类型。

下面是一个使用类型上界的示例代码:

abstract class Animal {
  def name: String
}
class Dog(val name: String) extends Animal {
  def bark(): Unit = println("Woof!")
}
class Cage[A <: Animal](val animal: A) {
  def showAnimal(): Unit = println(animal.name)
}
val dog: Dog = new Dog("Fido")
val cage: Cage[Animal] = new Cage[Dog](dog)
cage.showAnimal() // 输出:Fido

在上述示例中,定义了一个抽象类 Animal,以及它的子类 DogDog 类继承自 Animal 类,并实现了 name 方法。

然后,定义了一个泛型类 Cage[A <: Animal],它接受一个类型参数 A,并使用类型上界 A <: Animal 来确保 AAnimal 类型或其子类型。Cage 类有一个名为 animal 的属性,它的类型是 A。我们定义了一个名为 showAnimal() 的方法,它打印出 animal 的名称。

创建了一个 Dog 类型的对象 dog。然后,我们创建了一个 Cage[Animal] 类型的 cage,并将 dog 对象作为参数传递给它。

最后,调用 cageshowAnimal() 方法,它成功打印出了 Dog 对象的名称。

类型下界

类型下界(Lower Bounds): 类型下界用于指定泛型类型参数必须是某个类型或其父类型。我们使用 > 符号来定义类型下界。例如,A >: B 表示类型参数 A 必须是类型 B 或其父类型。

下面是一个使用类型下界的示例代码:

class Animal {
  def sound(): Unit = println("Animal sound")
}
class Dog extends Animal {
  override def sound(): Unit = println("Dog barking")
}
class Cat extends Animal {
  override def sound(): Unit = println("Cat meowing")
}
def makeSound[A >: Dog](animal: A): Unit = {
  animal.sound()
}
val dog: Dog = new Dog
val cat: Cat = new Cat
makeSound(dog) // 输出:Dog barking
makeSound(cat) // 输出:Animal sound

在上述示例中,定义了一个基类 Animal,以及两个子类 DogCat。这些类都有一个 sound() 方法,用于输出不同的动物声音。

接下来,定义了一个泛型函数 makeSound[A >: Dog](animal: A),其中类型参数 A 的下界被定义为 Dog,即 A >: Dog。这意味着 A 必须是 Dog 类型或其父类型。

makeSound() 函数内部,我们调用传入的 animal 对象的 sound() 方法。

然后,创建了一个 Dog 对象 dog 和一个 Cat 对象 cat

最后,分别调用 makeSound() 函数,并将 dogcat 作为参数传递进去。由于类型下界被定义为 Dog,所以 dog 参数符合条件,而 cat 参数被隐式地向上转型为 Animal,也满足条件。因此,调用 makeSound() 函数时,输出了不同的声音。

通过类型上界和类型下界,我们可以对泛型类型参数的范围进行限制,以确保类型的约束和类型安全性。这使得我们能够编写更灵活、可复用且类型安全的代码。

内部类

在 Scala 中,内部类是一个定义在另一个类内部的类。内部类可以访问外部类的成员,并具有更紧密的关联性。下面是一个关于 Scala 中内部类的解释和示例代码:

在 Scala 中,内部类可以分为两种类型:成员内部类(Member Inner Class)和局部内部类(Local Inner Class)。

成员内部类: 成员内部类是定义在外部类的作用域内,并可以直接访问外部类的成员(包括私有成员)。成员内部类可以使用外部类的实例来创建和访问。

下面是一个示例代码:

class Outer {
  private val outerField: Int = 10
  class Inner {
    def printOuterField(): Unit = {
      println(s"Outer field value: $outerField")
    }
  }
}
val outer: Outer = new Outer
val inner: outer.Inner = new outer.Inner
inner.printOuterField() // 输出:Outer field value: 10

在上述示例中,定义了一个外部类 Outer,它包含一个私有成员 outerField。内部类 Inner 定义在 Outer 的作用域内,并可以访问外部类的成员。

在主程序中,创建了外部类的实例 outer。然后,我们使用 outer.Inner 来创建内部类的实例 inner。注意,我们需要使用外部类的实例来创建内部类的实例。

最后,调用内部类 innerprintOuterField() 方法,它成功访问并打印了外部类的私有成员 outerField

局部内部类: 局部内部类是定义在方法或代码块内部的类。局部内部类的作用域仅限于所在方法或代码块内部,无法从外部访问。

下面是一个示例代码:

def outerMethod(): Unit = {
  val outerField: Int = 10
  class Inner {
    def printOuterField(): Unit = {
      println(s"Outer field value: $outerField")
    }
  }
  val inner: Inner = new Inner
  inner.printOuterField() // 输出:Outer field value: 10
}
outerMethod()

在上述示例中,定义了一个外部方法 outerMethod。在方法内部,我们定义了一个局部变量 outerField 和一个局部内部类 Inner

在方法内部,创建了内部类 Inner 的实例 inner。注意,内部类的作用域仅限于方法内部。

最后,调用内部类 innerprintOuterField() 方法,它成功访问并打印了外部变量 outerField

通过使用内部类,我们可以在 Scala 中实现更紧密的关联性和封装性,同时允许内部类访问外部类的成员。内部类在某些场景下可以提供更清晰和组织良好的。

复合类型

在 Scala 中,复合类型(Compound Types)允许我们定义一个类型,它同时具有多个特质(Traits)或类的特性。复合类型可以用于限制一个对象的类型,以便它同时具备多个特性。下面是关于复合类型的解释和示例代码:

复合类型使用 with 关键字将多个特质或类组合在一起,形成一个新的类型。

下面是一个示例代码:

trait Flyable {
  def fly(): Unit
}
trait Swimmable {
  def swim(): Unit
}
class Bird extends Flyable {
  override def fly(): Unit = println("Flying...")
}
class Fish extends Swimmable {
  override def swim(): Unit = println("Swimming...")
}
def action(obj: Flyable with Swimmable): Unit = {
  obj.fly()
  obj.swim()
}
val bird: Bird = new Bird
val fish: Fish = new Fish
action(bird) // 输出:Flying...
action(fish) // 输出:Swimming...

在上述示例中,定义了两个特质 FlyableSwimmable,分别表示可飞行和可游泳的特性。然后,我们定义了两个类 BirdFish,分别实现了相应的特质。

接下来,定义了一个方法 action,它接受一个类型为 Flyable with Swimmable 的参数。这表示参数必须同时具备 FlyableSwimmable 的特性。

在主程序中,创建了一个 Bird 对象 bird 和一个 Fish 对象 fish

最后,分别调用 action 方法,并将 birdfish 作为参数传递进去。由于它们都同时具备 FlyableSwimmable 的特性,所以可以成功调用 fly()swim() 方法。

通过使用复合类型,可以在 Scala 中定义一个类型,它同时具备多个特质或类的特性,从而实现更灵活和精确的类型约束。这有助于编写更可靠和可复用的代码。

多态方法

在 Scala 中,多态方法(Polymorphic Methods)允许我们定义可以接受多种类型参数的方法。这意味着同一个方法可以根据传入参数的类型执行不同的逻辑。下面是关于多态方法的解释和示例代码:

多态方法使用类型参数来定义方法的参数类型,并使用泛型来表示可以接受多种类型参数。在方法内部,可以根据类型参数的实际类型执行不同的逻辑。

下面是一个示例代码:

def printType[T](value: T): Unit = {
  value match {
    case s: String => println("String: " + s)
    case i: Int => println("Int: " + i)
    case d: Double => println("Double: " + d)
    case _ => println("Unknown type")
  }
}
printType("Hello") // 输出:String: Hello
printType(123) // 输出:Int: 123
printType(3.14) // 输出:Double: 3.14
printType(true) // 输出:Unknown type

在上述示例中,定义了一个多态方法 printType,它接受一个类型参数 T。根据传入参数的类型,我们使用模式匹配来判断其实际类型,并执行相应的逻辑。

在方法内部,使用 match 表达式对传入的参数 value 进行模式匹配。对于不同的类型,我们分别输出相应的类型信息。

在主程序中,多次调用 printType 方法,并传入不同类型的参数。根据传入的参数类型,方法会执行相应的逻辑并输出对应的类型信息。

函数

Scala中一个简单的函数定义如下,我们可以在Scala中使用JDK的类:

import java.util.Date
object Main {
  def main(args: Array[String]): Unit = {
    printCurrentDate() // 输出当前日期和时间
  }
  def printCurrentDate(): Unit = {
    val currentDate = new Date()
    println(currentDate.toString)
  }
}

函数默认值

在 Scala 中,可以为函数参数指定默认值。这样,当调用函数时如果没有提供参数值,将使用默认值。下面是一个简单的示例:

object Main {
  def main(args: Array[String]): Unit = {
    greet() // 输出 "Hello, World!"
    greet("Alice") // 输出 "Hello, Alice!"
  }
  def greet(name: String = "World"): Unit = {
    println(s"Hello, $name!")
  }
}

高阶函数

高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。在Scala中函数是“一等公民”,所以允许定义高阶函数。这里的术语可能有点让人困惑,我们约定,使用函数值作为参数,或者返回值为函数值的“函数”和“方法”,均称之为“高阶函数”。

def applyFuncToList(list: List[Int], f: Int => Int): List[Int] = {
  list.map(f)
}
val numbers = List(1, 2, 3, 4)
val double = (x: Int) => x * 2
val doubledNumbers = applyFuncToList(numbers, double) // List(2, 4, 6, 8)

在这个例子中,applyFuncToList 函数接受一个整数列表和一个函数 f,该函数将一个整数作为输入并返回一个整数。然后,applyFuncToList 函数使用 map 方法将函数 f 应用于列表中的每个元素。在上面的代码中,我们定义了一个 double 函数,它将输入乘以2,并将其传递给 applyFuncToList 函数以对数字列表中的每个元素进行加倍。

匿名函数

在 Scala 中,匿名函数是一种没有名称的函数,可以用来创建简洁的函数字面量。它们通常用于传递给高阶函数,或作为局部函数使用。

例如,下面是一个简单的匿名函数,它接受两个整数参数并返回它们的和:

object Main {
  def main(args: Array[String]): Unit = {
    val add = (x: Int, y: Int) => x + y
    println(add(1, 2))  //输出: 3
  }
}

偏应用函数

简单来说,偏应用函数就是一种只对输入值的某个子集进行处理的函数。它只会对符合特定条件的输入值进行处理,而对于不符合条件的输入值则会抛出异常。

举个例子:

object Main {
  def main(args: Array[String]): Unit = {
    println(divide.isDefinedAt(0)) // false
    println(divideSafe.isDefinedAt(0)) // true
    println(divide(1)) // 42
    println(divideSafe(1)) // Some(42)
    // println(divide(0)) // 抛出异常
    println(divideSafe(0)) // None
  }
  val divide: PartialFunction[Int, Int] = {
    case d: Int if d != 0 => 42 / d
  }
  val divideSafe: PartialFunction[Int, Option[Int]] = {
    case d: Int if d != 0 => Some(42 / d)
    case _ => None
  }
}

这个例子中,divide 是一个偏应用函数,它只定义了对非零整数的除法运算。如果我们尝试用 divide 函数去除以零,它会抛出一个异常。其中isDefinedAt 是一个方法,它用于检查偏应用函数是否在给定的输入值上定义。如果偏应用函数在给定的输入值上定义,那么 isDefinedAt 方法会返回 true,否则返回 false

为了避免这种情况,我们可以使用 divideSafe 函数,它返回一个 Option 类型的结果。如果除数为零,它会返回 None 而不是抛出异常。

柯里化函数

柯里化(Currying)是一种将多参数函数转换为一系列单参数函数的技术。我们可以使用柯里化来定义函数,例如:

def add(a: Int)(b: Int): Int = a + b

这个 add 函数接受两个参数 ab,并返回它们的和。由于它是一个柯里化函数,所以我们可以将它看作是一个接受单个参数 a 的函数,它返回一个接受单个参数 b 的函数。

我们可以这样调用这个函数:

val result = add(1)(2) // 3

或者这样:

val addOne = add(1) _
val result = addOne(2) // 3

在上面的例子中,我们首先调用 add 函数并传入第一个参数 1,然后我们得到一个新的函数 addOne,它接受一个参数并返回它与 1 的和。最后,我们调用 addOne 函数并传入参数 2,得到结果 3。

柯里化函数可以帮助我们实现参数复用和延迟执行等功能。

柯里化函数的好处之一是它可以让我们给一个函数传递较少的参数,得到一个已经记住了某些固定参数的新函数。这样,我们就可以在不同的地方使用这个新函数,而不需要每次都传递相同的参数²。

此外,柯里化函数还可以帮助我们实现函数的延迟计算。当我们传递部分参数时,它会返回一个新的函数,可以在新的函数中继续传递后面的参数。这样,我们就可以根据需要来决定何时执行这个函数。

惰性函数

可以使用 lazy 关键字定义惰性函数。惰性函数的执行会被推迟,直到我们首次对其取值时才会执行。

下面是一个简单的例子,展示了如何定义和使用惰性函数:

def sum(x: Int, y: Int): Int = {
  println("sum函数被执行了...")
  x + y
}
lazy val res: Int = sum(1, 2)
println("-----")
println(res)

在这个例子中,我们定义了一个函数 sum,它接受两个参数并返回它们的和。然后我们定义了一个惰性值 res 并将其赋值为 sum(1, 2)

在主程序中,我们首先打印了一行分隔符。然后我们打印了变量 res 的值。由于 res 是一个惰性值,因此在打印它之前,函数 sum 并没有被执行。只有当我们首次对 res 取值时,函数 sum 才会被执行。

这就是Scala中惰性函数的基本用法。你可以使用 lazy 关键字定义惰性函数,让函数的执行被推迟。

本篇文章就到这里,感谢阅读,如果本篇博客有任何错误和建议,欢迎给我留言指正。文章持续更新,

相关实践学习
基于MaxCompute的热门话题分析
本实验围绕社交用户发布的文章做了详尽的分析,通过分析能得到用户群体年龄分布,性别分布,地理位置分布,以及热门话题的热度。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
目录
相关文章
|
3月前
|
分布式计算 大数据 Java
大数据-87 Spark 集群 案例学习 Spark Scala 案例 手写计算圆周率、计算共同好友
大数据-87 Spark 集群 案例学习 Spark Scala 案例 手写计算圆周率、计算共同好友
88 5
|
3月前
|
分布式计算 关系型数据库 MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
大数据-88 Spark 集群 案例学习 Spark Scala 案例 SuperWordCount 计算结果数据写入MySQL
65 3
|
3月前
|
消息中间件 分布式计算 NoSQL
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
大数据-104 Spark Streaming Kafka Offset Scala实现Redis管理Offset并更新
61 0
|
3月前
|
消息中间件 存储 分布式计算
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
大数据-103 Spark Streaming Kafka Offset管理详解 Scala自定义Offset
118 0
|
3月前
|
Java 大数据 数据库连接
大数据-163 Apache Kylin 全量增量Cube的构建 手动触发合并 JDBC 操作 Scala
大数据-163 Apache Kylin 全量增量Cube的构建 手动触发合并 JDBC 操作 Scala
67 2
大数据-163 Apache Kylin 全量增量Cube的构建 手动触发合并 JDBC 操作 Scala
|
3月前
|
分布式计算 大数据 Java
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
62 1
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
|
3月前
|
消息中间件 存储 druid
大数据-156 Apache Druid 案例实战 Scala Kafka 订单统计
大数据-156 Apache Druid 案例实战 Scala Kafka 订单统计
67 3
|
3月前
|
SQL 分布式计算 Java
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
大数据-96 Spark 集群 SparkSQL Scala编写SQL操作SparkSQL的数据源:JSON、CSV、JDBC、Hive
92 0
|
3月前
|
缓存 分布式计算 大数据
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(一)
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(一)
81 0
|
3月前
|
分布式计算 算法 大数据
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(二)
大数据-90 Spark 集群 RDD 编程-高阶 RDD容错机制、RDD的分区、自定义分区器(Scala编写)、RDD创建方式(二)
75 0

热门文章

最新文章