Scala编程进阶

跳出循环语句的3种方法... 1

多维数组... 2

Java数组与Scala数组缓冲的隐式转换... 2

Java Map与Scala Map的隐式转换... 3

Tuple拉链操作... 3

内部类的作用域:外部类对象... 3

扩大内部类作用域:伴生对象... 4

扩大内部类作用域:类型投影... 4

内部类获取外部类的引用... 4

package定义... 5

package特性... 5

import. 8

   

跳出循环语句的3种方法

方法一:使用boolean控制变量

while循环:

var flag = true

var res = 0

var n = 0

while(flag) {

res += n

n += 1

if (n == 5) {

flag = false

}

}

for循环:(高级for循环,加上了if守卫)

var flag = true

var res = 0

for (i <- 0 until 10 if flag) {

res += i

if (i == 4) flag = false

}

方法二:在嵌套函数中使用return

def add_outer() = {

var res = 0

def add_inner() {

for (i <- 0 until 10)
{

if (i == 5)
{

return

}

res += i

}

}

add_inner()

res

}

方法三:使用Breaks对象的break方法

跟java里面的break比较类似,相对来说,比较灵活好用;与breakable代码块配合使用

import scala.util.control.Breaks._

var res = 0

breakable {

for (i <- 0 until 10) {

if (i == 5) {

break;

}

res += i

}

}

多维数组

什么是多维数组?:数组的元素,还是数组,数组套数组,就是多维数组

构造指定行与列的二维数组:Array.ofDim方法

val multiDimArr1 = Array.ofDim[Double](3,
4)

multiDimArr1(0)(0) = 1.0

构造不规则多维数组:

val multiDimArr2 = new Array[Array[Int]](3)

multiDimArr2(0) = new Array[Int] (1)

multiDimArr2(1) = new Array[Int] (2)

multiDimArr2(2) = new Array[Int] (3)

multiDimArr2(1)(1) = 1

Java数组与Scala数组缓冲的隐式转换

Scala代码中,直接调用JDK(Java)的API,比如调用一个Java类的方法,势必可能会传入Java类型的list,此时如果直接把Scala的ArrayBuffer传入Java接收ArrayList的方法,肯定不行。这可以先将Scala中的Buffer转换为Java中的List即可

import
scala.collection.JavaConversions.bufferAsJavaList

import scala.collection.mutable.ArrayBuffer

val command =
ArrayBuffer("javac",
"C:\\Users\\Administrator\\Desktop\\HelloWorld.java") // 调用操作系统命令编译源码

// ProcessBuilder为JDK中的类,构造函数为:ProcessBuilder(List<String>
command),要求的是List,所以需要将Scala的Buffer转换为Java中的List,才能在Java中使用

val processBuilder = new
ProcessBuilder(command)

val process = processBuilder.start()

val res = process.waitFor()

下面是将Java返回的List转换为Buffer:

import scala.collection.JavaConversions.asScalaBuffer

import scala.collection.mutable.Buffer

// ProcessBuilder的command()方法返回的是List<String>,所以需要将List<String>隐式转换为Buffer[String],才能在Scala中使用

val cmd: Buffer[String] =
processBuilder.command()

Java Map与Scala
Map的隐式转换

import scala.collection.JavaConversions.mapAsScalaMap

val javaScores = new java.util.HashMap[String, Int]()

javaScores.put("Alice", 10)

javaScores.put("Bob", 3)

javaScores.put("Cindy", 8)

val scalaScores: scala.collection.mutable.Map[String,
Int] = javaScores // Java Map自动隐式转换 Scala Map

import
scala.collection.JavaConversions.mapAsJavaMap

import java.awt.font.TextAttribute._

val scalaAttrMap = Map(FAMILY ->
"Serif", SIZE -> 12)

val font = new java.awt.Font(scalaAttrMap)
// Scala Map自动隐式转换
Java Map

Tuple拉链操作

Tuple拉链操作指的就是zip操作

zip操作,是Array类的方法,用于将两个Array,合并为一个Array

比如Array(v1)和Array(v2),使用zip操作合并后的格式为Array((v1,v2))

合并后的Array的元素类型为Tuple

val students = Array("Leo",
"Jack", "Jen")

val scores = Array(80, 100, 90)

val studentScores = students.zip(scores)

for ((student, score) <- studentScores)

println(student + " " +
score)

如果Array的元素类型是个Tuple,调用Array的toMap方法,可以将Array转换为Map

studentScores.toMap

内部类的作用域:外部类对象

import scala.collection.mutable.ArrayBuffer

class Class {

class Student(val name: String)

val students = new
ArrayBuffer[Student]

def register(name: String) =  {

new Student(name)

}

}

val c1 = new Class

val leo = c1.register("leo")

c1.students += leo

val c2 = new Class

val jack = c2.register("jack")

c1.students += jack // error: type
mismatch;

扩大内部类作用域:伴生对象

object Class {

class Student(val name: String)

}

class Class {

val students = new ArrayBuffer[Class.Student]

def register(name: String) = {

new Class.Student(name)

}

}

val c1 = new Class

val leo = c1.register("leo")

c1.students += leo

val c2 = new Class

val jack = c2.register("jack")

c1.students += jack

扩大内部类作用域:类型投影

class Class {

class Student(val name: String)

val students = new ArrayBuffer[Class#Student] // 明确说明使用的是Class类型中的Student类,而非Class对象中的

def register(name: String) =  {

new Student(name)

}

}

val c1 = new Class

val leo = c1.register("leo")

c1.students += leo

val c2 = new Class

val jack = c2.register("jack")

c1.students += jack

内部类获取外部类的引用

class Class(val name: String) {

outer =>
//名随便

class Student(val
name: String) {

def introduceMyself =
"Hello, I‘m " + name + ", I‘m very happy to join class " + outer.name

}

def register(name: String) =  {

new Student(name)

}

}

val c1 = new Class("c1")

val leo = c1.register("leo")

leo.introduceMyself

package定义

因为要对多个同名的类进行命名空间的管理,避免同名类发生冲突

比如说,scala.collection.mutable.Map和scala.collection.immutable.Map

package定义的第一种方式: 多层级package定义(比较差的做法,一般不这么干)

package com {

package
sn {

package
scala {

class
Test {}

}

}

}

package定义的第二种方式: 串联式package定义(也不怎么样,一般也不这么干)

package com.sn.scala {

package
service {

class
Test {}

}

}

package定义的第三种方式: 文件顶部package定义

package com.sn.scala.service

class Test {

}

package特性

同一个包定义,可以在不同的scala源文件中的:

Test1.scala

package com {

package sn {

package scala {

class Test1

}

}

}

Test2.scala

package com {

package sn {

package scala {

class Test2

}

}

}

一个scala源文件内,可以包含两个包:

Test3.scala

package com {

package sn {

package scala1 {

class Test

}

}

}

package com {

package sn {

package scala2 {

class Test

}

}

}

子包中的类,可以访问父包中的类:

Test.scala

package com {

package sn {

package scala {

object Utils {

def isNotEmpty(str:
String): Boolean = str != null && str != ""

}

class Test

package service {

class MyService {

def sayHello(name:
String) {

if
(Utils.isNotEmpty(name)) {

println("Hello, " + name)

} else {

println("Who are you?")

}

}

}

}

}

}

}

object T {

def main(args:
Array[String]) {

import
com.sn.scala.service._

new
MyService().sayHello("")

new
MyService().sayHello("leo")

}

}

相对包名与绝对包名:

package com {

package sn {

package scala {

object Utils {

def isNotEmpty(str:
String): Boolean = str != null && str != ""

}

class Test

package collection {}

package service {

class MyService {

//
报错,默认使用相对报名,从com.sn.scala.collection包中,寻找mutable包下的ArrayBuffer类

//
但是找不到,所以会报错

//
val names = new scala.collection.mutable.ArrayBuffer[String]

//
正确的做法是使用_root_,引用绝对包名

val names = new _root_.scala.collection.mutable.ArrayBuffer[String]

def sayHello(name:
String) {

if
(Utils.isNotEmpty(name)) {

println("Hello, " + name)

} else {

println("Who are you?")

}

}

}

}

}

}

}

定义package对象(比较少用):

package内的成员,可以直接访问package对象内的成员

package com.sn.scala

package object service {

val defaultName = "Somebody"

}

package service {

class MyService {

def sayHello(name:
String) {

if (name != null && name
!= "") {

println("Hello, " + name)

} else {

println("Hello, " + defaultName)//访问包对象中的成员

}

}

}

}

package可见性:

package com.sn {

package scala {

class Person {

//com.sn.scala包下可见

private[scala] val name = "leo"

//com.sn包下可见

private[sn] val age = 25

}

object T1 {

new Person().name

}

}

object T2 {

import
com.sn.scala.Person

new Person().age

}

}

import

package com.sn.scala

package service {

class MyService {

def sayHello(name:
String) {}

}

}

import
com.sn.scala.service.MyService;

object MainClass {

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

val service = new MyService

}

}

import特性一: 用import
com.sn.scala.service._这种格式,可以导入包下所有的成员

import特性二: scala与java不同之处在于,任何地方都可以使用import,比如类内、方法内,这种方式的好处在于,可以在一定作用域范围内使用导入

object MainClass {

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

import
com.sn.scala.service._

val service = new MyService

}

}

import特性三: 选择器、重命名、隐藏

import com.sn.scala.service.{ MyService },仅仅导入com.sn.scala.service包下的MyService类,其它不导入

import com.sn.scala.service.{ MyService
=> MyServiceImpl },将导入的类进行重命名

import com.sn.scala.service.{ MyService
=> _, _ },导入com.sn.scala.service包下所有的类,但是隐藏掉MyService类

import特性四: 隐式导入

每个scala程序默认都会隐式导入以下几个包下所有的成员

import java.lang._

import scala._

import Predef._

重写field的提前定义

默认情况下,如果父类中的构造函数代码用到了被子类重写(或实现)的field,那么可能会出现未被正确初始化的问题:

当父类的构造函数执行时,如果使用到了被子类实现或重写过的field(field重写或实现相当于对应的getter方法被重写),会调用子类重写或实现过的field,由于子类构造器还没有执行,所以会返回子类中被重写或实现过的该field的初始值(比如Int为0,String为null)。详细可以参数《Scala编程基础》中的“trait field的初始化”相应章节

class Student {

val classNumber: Int = 10

val classScores: Array[Int] = new Array[Int](classNumber) // 会调用子类被重写过的classNumber字段

}

class PEStudent extends Student {

override val classNumber: Int = 3

}

scala> new PEStudent().classScores

res42: Array[Int] = Array()

本来我们期望的是,PEStudent,可以从Student继承来一个长度为3的classScores数组,结果PEStudent对象,只有一个长度为0的classScores数组

此时只能使用Scala对象继承的一个高级特性: 提前定义,在父类构造函数执行之前,先执行子类的构造函数中的某些代码

class Student {

val classNumber: Int = 10

val classScores: Array[Int] = new Array[Int](classNumber) // 会调用子类被重写过的classNumber字段

}

class PEStudent extends {

override val classNumber: Int = 3

} with Student

scala> new
PEStudent().classScores

res43: Array[Int]
= Array(0, 0, 0)

也可以这样,但Student需定义成trait:

trait Student {

val classNumber: Int = 10

val classScores: Array[Int] = new Array[Int](classNumber)

}

class PEStudent extends Student {

}

var ps = new {override val classNumber:
Int = 3} with PEStudent with Student

ps.classScores

Scala的继承层级

这里我们大概知道一下Scala的继承层级,我们写的所有的Scala trait和class,都是默认继承自一些Scala根类的,有一些基础的方法

Scala中,最顶端的两个trait是Nothing和Null,Null trait唯一的对象就是null

其次是继承了Nothing
trait的Any类

接着Anyval
trait和AnyRef类,都继承自Any类

Any类是个比较重要的类,其中定义了isInstanceOf和asInstanceOf等方法,以及equals、hashCode等对象的基本方法

Any类,有点像Java中的Object基类

AnyRef类,增加了一些多线程的方法,比如wait、notify/notifyAll、synchronized等,也是属于Java Object类的一部分

对象相等性

这里,我们要知道,在scala中,你如何判断两个引用变量,是否指向同一个对象实例

AnyRef的eq方法用于检查两个变量是否指向同一个对象实例

AnyRef的equals方法默认调用eq方法实现,也就是说,默认情况下,判断两个变量相等,要求必须指向同一个对象实例

通常情况下,自己可以重写equals方法,根据类的fields来判定是否相等

此外,定义equals方法时,也最好使用同样的fields,重写hashCode方法

如果只是想要简单地通过是否指向同一个对象实例,判定变量是否相当,那么直接使用==操作符即可,默认判断null,然后调用equals方法

class Product(val name: String, val price: Double) {

final override def equals(other:
Any) = {

val that =
other.asInstanceOf[Product]

if (that == null) false

else name == that.name && price == that.price

}

final override def hashCode = 13 * name.hashCode + 17 * price.hashCode

}

附件列表

时间: 2024-08-08 08:19:26

Scala编程进阶的相关文章

(升级版)Spark从入门到精通(Scala编程、案例实战、高级特性、Spark内核源码剖析、Hadoop高端)

本课程主要讲解目前大数据领域最热门.最火爆.最有前景的技术——Spark.在本课程中,会从浅入深,基于大量案例实战,深度剖析和讲解Spark,并且会包含完全从企业真实复杂业务需求中抽取出的案例实战.课程会涵盖Scala编程详解.Spark核心编程.Spark SQL和Spark Streaming.Spark内核以及源码剖析.性能调优.企业级案例实战等部分.完全从零起步,让学员可以一站式精通Spark企业级大数据开发,提升自己的职场竞争力,实现更好的升职或者跳槽,或者从j2ee等传统软件开发工程

Scala函数式编程进阶

1 package com.dtspark.scala.basics 2 3 /** 4 * 函数式编程进阶: 5 * 1,函数和变量一样作为Scala语言的一等公民,函数可以直接赋值给变量: 6 * 2, 函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是如果你要使用的话,一般会把这个匿名函数赋值给一个变量(其实是val常量),Spark源码中大量存在这种语法,必须掌握: 7 * 3, 函数可以作为参数直接传递给函数,这极大的简化的编程的语法,为什么这

第73讲:Scala界面和事件处理编程进阶实战

今天学习了王家林老师讲解的scala编程的第73讲,主要是文件选择器的使用.让我们通过代码来亲身体验一下. import scala.swing._import java.io.Fileimport scala.swing.event.ButtonClickedimport scala.swing.Label object GUI_Event extends SimpleSwingApplication{  val fileChooser = new FileChooser(new File("

Spark函数式编程进阶

函数式编程进阶 1.函数和变量一样作为Scala语言的一等公民,函数可以直接复制给变量: 2.函数更长用的方式是匿名函数,定义的时候只需要说明输入参数的类型和函数体即可,不需要名称,但是匿名函数赋值给一个变量(其实是val常量),Spark源码中大量存在这种语法: 3.函数可以作为参数直接传递给函数,这极大地简化的编程语法: 4.函数式编程一个非常强大的地方之一在于函数的返回值可以是函数,当函数的返回类型是函数的时候,这个时候就是表明Scala的函数是实现了闭包! Scala壁报的内幕是:Sca

Spark2.0从入门到精通:Scala编程、大数据开发、上百个实战案例、内核源码深度剖析视频教程

38套大数据,云计算,架构,数据分析师,Hadoop,Spark,Storm,Kafka,人工智能,机器学习,深度学习,项目实战视频教程 视频课程包含: 38套大数据和人工智能精品高级课包含:大数据,云计算,架构,数据挖掘实战,实时推荐系统实战,电视收视率项目实战,实时流统计项目实战,离线电商分析项目实战,Spark大型项目实战用户分析,智能客户系统项目实战,Linux基础,Hadoop,Spark,Storm,Docker,Mapreduce,Kafka,Flume,OpenStack,Hiv

大数据技术之_19_Spark学习_02_Spark Core 应用解析+ RDD 概念 + RDD 编程 + 键值对 RDD + 数据读取与保存主要方式 + RDD 编程进阶 + Spark Core 实例练习

第1章 RDD 概念1.1 RDD 为什么会产生1.2 RDD 概述1.2.1 什么是 RDD1.2.2 RDD 的属性1.3 RDD 弹性1.4 RDD 特点1.4.1 分区1.4.2 只读1.4.3 依赖1.4.4 缓存1.4.5 CheckPoint第2章 RDD 编程2.1 RDD 编程模型2.2 RDD 创建2.2.1 由一个已经存在的 Scala 集合创建,即集合并行化(测试用)2.2.2 由外部存储系统的数据集创建(开发用)2.3 RDD 编程2.3.1 Transformatio

【Scala编程】函数式风格编写排序算法

有关Scala编程实例 在刚开始学习一门编程语言的时候,总是想去写一些比较大的程序和项目,但是由于基础不扎实,往往欲速则不达.所以,只能一步一步来,通过一些经典的小例子来实践和锻炼,最终不断加深编程的技能,坚持下来,相信慢慢就能够变得熟练. 冒泡排序.选择排序.插入排序的一般写法 这三种排序方法没有太多要说的东西,这里的编程方式和指定式方式没什么差别. 这里之所以使用Array数据,因为Array数组是可变的对象序列,其元素值可以改变,而List类对象的元素是不可变的. 这里的写法不是函数式风格

Python基础-第七天-面向对象编程进阶和Socket编程简介

本篇内容: 1.面向对象编程进阶-静态方法 2.面向对象编程进阶-类方法 3.面向对象编程进阶-属性方法 4.面向对象编程进阶-特殊成员(内置方法) 5.面向对象编程进阶-反射 6.异常处理.断言 7.Socket编程简介 一.面向对象编程进阶-静态方法 1.静态方法的实现 通过@staticmethod装饰器可以把其装饰的方法变为一个静态方法: 变成静态方法后,形参中可以不用写self了.如果写了self,默认是不会把对象本身传递给self,需要手动传递: class Dog(object):

shell脚本编程进阶练习题

这两天学习了shell脚本编程进阶,作为一枚文科生,小编觉得...恩..脚本很烧脑.....,不过小编还是做了些题,稍作总结后,呈给各位看官,内容如下: 一.条件选择if语句 选择执行: 注意:if语句可嵌套 单分支 if 判断条件;then 条件为真的分支代码 fi 双分支 if 判断条件; then 条件为真的分支代码 else 条件为假的分支代码 fi 多分支 if 判断条件1; then 条件为真的分支代码 elif 判断条件2; then 条件为真的分支代码 elif 判断条件3; t