【大数据】初识Scala 4

简介: 【大数据】初识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 关键字定义惰性函数,让函数的执行被推迟。

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

相关实践学习
简单用户画像分析
本场景主要介绍基于海量日志数据进行简单用户画像分析为背景,如何通过使用DataWorks完成数据采集 、加工数据、配置数据质量监控和数据可视化展现等任务。
SaaS 模式云数据仓库必修课
本课程由阿里云开发者社区和阿里云大数据团队共同出品,是SaaS模式云原生数据仓库领导者MaxCompute核心课程。本课程由阿里云资深产品和技术专家们从概念到方法,从场景到实践,体系化的将阿里巴巴飞天大数据平台10多年的经过验证的方法与实践深入浅出的讲给开发者们。帮助大数据开发者快速了解并掌握SaaS模式的云原生的数据仓库,助力开发者学习了解先进的技术栈,并能在实际业务中敏捷的进行大数据分析,赋能企业业务。 通过本课程可以了解SaaS模式云原生数据仓库领导者MaxCompute核心功能及典型适用场景,可应用MaxCompute实现数仓搭建,快速进行大数据分析。适合大数据工程师、大数据分析师 大量数据需要处理、存储和管理,需要搭建数据仓库?学它! 没有足够人员和经验来运维大数据平台,不想自建IDC买机器,需要免运维的大数据平台?会SQL就等于会大数据?学它! 想知道大数据用得对不对,想用更少的钱得到持续演进的数仓能力?获得极致弹性的计算资源和更好的性能,以及持续保护数据安全的生产环境?学它! 想要获得灵活的分析能力,快速洞察数据规律特征?想要兼得数据湖的灵活性与数据仓库的成长性?学它! 出品人:阿里云大数据产品及研发团队专家 产品 MaxCompute 官网 https://www.aliyun.com/product/odps&nbsp;
目录
相关文章
|
9月前
|
大数据 编译器 Scala
【大数据】初识Scala 2
【大数据】初识Scala
64 0
|
5月前
|
大数据 Scala
大数据生态思维导图____2021最新最全Scala语法思维导图!(待更新)
大数据生态思维导图____2021最新最全Scala语法思维导图!(待更新)
19 0
|
9月前
|
大数据 Java Scala
【大数据】初识Scala 3
【大数据】初识Scala
51 0
|
9月前
|
分布式计算 Java 大数据
【大数据】初识Scala 1
【大数据】初识Scala
63 0
|
12月前
|
存储 大数据 Scala
大数据开发基础的编程语言的Scala的Actor编程
当谈到大数据开发时,Scala是一个非常流行的编程语言。Scala是一种静态类型的编程语言,它结合了面向对象和函数式编程范型。Scala为大数据处理提供了强大的支持,因此在许多大数据项目中被广泛使用。其中,Scala的Actor编程模型可以帮助我们设计和实现高效的并发系统。
71 0
|
12月前
|
Java 大数据 Scala
大数据开发基础的编程语言的Scala的基础语法
Scala是一种基于JVM的多范式编程语言,它支持面向对象编程和函数式编程,并具有强大的类型推导机制和静态类型检查。本文将介绍Scala的基础语法,帮助开发者快速入门这门语言。
58 0
|
12月前
|
安全 Java 大数据
大数据开发基础的编程语言的Scala的类型系统
Scala是一种强类型的编程语言,它具有一套完善的类型系统。本文将介绍Scala的类型系统,帮助开发者了解这门语言的类型安全性和灵活性。
73 0
|
Java 大数据 Scala
大数据开发基础的编程语言的Scala的类和对象
Scala是一种基于JVM的编程语言,它支持面向对象编程和函数式编程。本文将介绍Scala中类和对象的概念和用法,帮助开发者更好地理解和应用这门语言。
48 0
|
大数据 Serverless Scala
大数据开发基础的编程语言的Scala的函数和闭包
Scala是一种支持函数式编程的编程语言,它具有强大的函数和闭包功能。本文将介绍Scala中函数和闭包的概念和用法,帮助开发者更好地理解和应用这门语言。
55 0
|
19天前
|
数据采集 SQL 数据可视化
大数据可视化技巧:借助PowerBI提升数据故事讲述力
【4月更文挑战第8天】Power BI助力大数据可视化,支持多种数据源连接,如SQL Server、Excel,提供数据清洗与转换功能。通过选择合适图表类型、运用颜色和大小强化表达,创建交互式仪表板。讲述数据故事时,注重故事主线设计,利用叙事技巧引导观众,并添加文本说明。分享已完成报告,提升数据驱动决策能力。动手实践,体验Power BI的强大与易用。
35 0