scala高级
**
- .Mixin–混编
当某个特质被用于组合类时,被称为混入;
Java:当一个类有接口的时候称为混入;
trait可以继承自类;
类实现接口在java中使用(implements),在scala中使用with;
一个类只能有一个父类但是可以有多个混入(分别使用关键字extend和with)
混入和某个父类可能有相同的父类
-
高阶函数
-高阶函数是指使用其他函数作为参数、或者返回一个函数作为结果的函数。在Scala中函数是“一等公民”,所以允许定义高阶函数。这里的术语可能有点让人困惑,我们约定,使用函数值作为参数,或者返回值为函数值的“函数”和“方法”,均称之为“高阶函数”。
方法的参数和返回值在java中是一个类型(基本数据类型和引用类型),类型可以再增加一个方法;如果java支持此语法,那scala就木有必要学
方法的参数和返回值可以是方法;
使用高阶函数的一个原因是减少冗余的代码
返回函数的函数:方法的返回值也是函数; -
柯里化–多参数列表
方法可以定义多个参数列表,当使用较少的参数列表调用多参数列表的方法时,会产生一个新的函数,该函数接收剩余的参数列表作为其参数。这被称为柯里化。 -
案例类
案例类非常适合用于不可变的数据
不能使用new来创建对象,这是因为案例类有一个默认的apply方法来负责对象的创建。
使用==比较,可以直接比较堆和栈的上的值
当你创建包含参数的案例类时,这些参数是公开(public)的val,不能为案例类的属性重新赋值
拷贝:浅拷贝,只拷贝类的属性中(基本数据类型);深拷贝(不但拷贝基本数据类型还要拷贝引用数据类型)
密封类:
特质(trait)和类(class)可以用sealed标记为密封的,这意味着其所有子类都必须与之定义在相同文件中,从而保证所有子类型都是已知的。 -
提取器对象
提取器对象是一个包含有 unapply 方法的单例对象。apply 方法就像一个构造器,接受参数然后创建一个实例对象,反之 unapply 方法接受一个实例对象然后返回最初创建它所用的参数。提取器常用在模式匹配和偏函数中。
unapply 方法的返回值应当符合下面的某一条:
如果只是用来判断真假,可以返回一个 Boolean 类型的值。例如 case even()。
如果只是用来提取单个 T 类型的值,可以返回 Option[T]。(Option===java中的枚举)
如果你想要提取多个值,类型分别为 T1,…,Tn,可以把它们放在一个可选的元组中 Option[(T1,…,Tn)]。
有时,要提取的值的数量不是固定的,因此我们想根据输入来返回随机数量的值。这种情况下,你可以用 unapplySeq 方法来定义提取器,此方法返回 Option[Seq[T]]。常见的例子有,用 case List(x, y, z) => 来解构一个列表 List,以及用一个正则表达式 Regex 来分解一个字符串 String,例如 case r(name, remainingFields @ _*) =>。 -
.For—yield
Scala 提供一个轻量级的标记方式用来表示 序列推导。推导使用形式为 for (enumerators) yield e 的 for 表达式,此处 enumerators 指一组以分号分隔的枚举器。一个 enumerator 要么是一个产生新变量的生成器,要么是一个过滤器。for 表达式在枚举器产生的每一次绑定中都会计算 e 值,并在循环结束后返回这些值组成的序列。
-
泛型
在java中定义的时候使用的<>, 这里面的字母随便写,一般是一个字母,
泛型类指可以接受类型参数的类。泛型类在集合类中被广泛使用。使用的是[ ]
一个惯例是使用字母 A 作为参数标识符,当然你可以使用任何参数名称。
型变:
[+A]:协变;在定义的时候使用的是+a,在使用的时候定义的是一个父类,验证的时候传入子类也木有问题;
[-A]:逆变:在定义的时候使用的是-a,在使用的时候定义的是一个子类,验证的时候传入父类也木有问题;
[A]:不变;在定义的时候使用的是a,在使用的时候定义的是一个类,验证的时候传入此类也木有问题;
java的泛型默认是+a; - 复合类型
- def cloneAndReset(obj: Cloneable with Resetable): Cloneable = { //… }
自类型
自类型===this
- 隐式参数
- 方法可以具有 隐式 参数列表,由参数列表开头的 implicit 关键字标记。 如果参数列表中的参数没有像往常一样传递, Scala 将查看它是否可以获得正确类型的隐式值,如果可以,则自动传递。
Scala 将查找这些参数的位置分为两类:
Scala 在调用包含有隐式参数块的方法时,将首先查找可以直接访问的隐式定义和隐式参数 (无前缀)。
然后,它在所有伴生对象中查找与隐式候选类型相关的有隐式标记的成员。
***-
-
隐式转换
一个从类型 S 到类型 T 的隐式转换由一个函数类型 S => T 的隐式值来定义,或者由一个可转换成所需值的隐式方法来定义。
隐式转换在两种情况下会用到:
如果一个表达式 e 的类型为 S, 并且类型 S 不符合表达式的期望类型 T。
在一个类型为 S 的实例对象 e 中调用 e.m, 如果被调用的 m 并没有在类型 S 中声明。
-
多态方法
Scala 中的方法可以按类型和值进行参数化。 语法和泛型类类似。 类型参数括在方括号中,而值参数括在圆括号中 -
代码:
def listOfDuplicates[A](x: A, length: Int): List[A] = {
if (length < 1)
Nil
else
x :: listOfDuplicates(x, length - 1)
}
println(listOfDuplicates[Int](3, 4)) // List(3, 3, 3, 3)
println(listOfDuplicates("La", 8)) // List(La, La, La, La, La
println(listOfDuplicates[String]("La", 8)) ,
println(listOfDuplicates[Animal](new Dog(), 8)) ,
-
运算符
Scala中好多方法的方法名是一个运算符 -
传名参数
传名参数 仅在被使用时触发实际参数的求值运算。 它们与 传值参数 正好相反。 要将一个参数变为传名参数,只需在它的类型前加上 =>。
传名参数的优点是,如果它们在函数体中未被使用,则不会对它们进行求值。 另一方面,传值参数的优点是它们仅被计算一次。 -
包对象
Scala 提供包对象作为在整个包中方便的共享使用的容器。
包对象中可以定义任何内容,而不仅仅是变量和方法。 例如,包对象经常用于保存包级作用域的类型别名和隐式转换。 包对象甚至可以继承 Scala 的类和特质。
按照惯例,包对象的代码通常放在名为 package.scala 的源文件中。
每个包都允许有一个包对象。 在包对象中的任何定义都被认为是包自身的成员
实战
- 高阶函数
package com.jinghangzz.scala.test
/**
* 高阶函数
*/
object HightFunc {
/**
* 第一个方法
* 参数是一个int类型的数
*/
def one(x: Int=100)
{
println("==one==" + x) ;
}
/**
* 第二个方法
* 参数是一个方法
* :后面是定义方法的结构(木有方法体)
* y:指的是参数(two)方法的参数(y)
* Unit:参数(two)方法的返回值
* 形参:是在方法定义处起的名字
*/
def two(x: (Int) => Unit)
{
println("==two==" + x) ;
/* 调用一下参数(参数是一个方法)
* 形参是一个方法,在此处可以执行方法
* 不知道参数函数的方法体
* */
x(200);
}
/**
* 方法的返回值也是函数
*/
def three(x: (Int) => Unit) :(Int) => Unit =
{
println("===three===" + x);
/*
* 方法的返回值是一个函数(方法)
* 发现地的返回值和参数都是函数;两个函数的参数和返回值是一样的;
* java中,要想把返回值给方法,必须要加上return ;
* 在scala中,可以省略;
* */
return x ;
}
/**
* 这是一个main函数
* :和Unit可以省略;只要返回值是Unit,都可以省
*/
def main(args: Array[String]): Unit =
{
println("==明成祖---永乐帝---朱棣==");
/* 调用two方法
* two方法的参数是一个方法,而刚好one就是一个方法
* 实参:在调用处传的值(one)
* 高阶函数会自动的把方法变成函数
* */
HightFunc.two(one);
/* 高阶函数,参数是函数(方法),参数是匿名函数
* 如果函数的参数只有一个,小括号可以省略;
* */
HightFunc.two( y => println("===匿名函数==" + y));
/* 简便
* 既然Scala编译器已经知道了参数的类型(一个单独的Int),
* 你可以只给出函数的右半部分,不过需要使用_代替参数名(在上一个例子中是x)
* */
HightFunc.two( y => println("===匿名函数aaaa==" + _));
/* 省略=>左边的代码,是因为已经确定了形参是什么类型,
* _表示此参数的类型,就不要让此参数的类型再发生变化 */
println("====================");
HightFunc.two(println(_)) ;
/* 调用three方法
* three方法的参数和返回值都是方法(函数)
* 通过查看three的源码发现,one和funRet是一回事
* */
var funRet = HightFunc.three(one);
println("=====three======main=========" + funRet);
funRet(300);
/**
* 第四个方法
* 这个只是定义了方法,木有调用
*/
def four()
{
println("===four===");
};
/* 调用方法 */
//four();
}
}
- .提取器
代码:
package com.jinghangzz.scala.test
import scala.util.Random
object ExectObj {
/**
* 描述的构建对象的过程
* 参数是字符串
* 返回是一个对象;
*/
def apply(name: String) =
{
/* 传入的名字+随机数 */
//name + Random.nextInt();
s"$name==${Random.nextInt}"
}
/**
* 参数是一个对象,
* 返回值是构建此对象的参数
* 提取器的返回值必须一个方法是get
*/
def unapply(name: String) : Option[String] =
{
var names:Array[String] = name.split("==");
if(names.tail.nonEmpty)
{
Some(names.apply(0))
}else
{
None;
}
}
/**
* 主函数
*/
def main(args: Array[String]): Unit = {
println("==明仁宗---洪熙帝---朱高炽==")
/* 构建了一个对象
* 默认的调用apply方法
* */
var na = ExectObj("洪熙帝");
/*var name = ExectObj.apply("洪熙帝");*/
println("==apply==" + na);
/* -----------调用unapply方法----------- */
/*val ExectObj(name) = na ;
println("=unapply=name==" + name);*/
/* 最全的写法 */
var name = ExectObj.unapply(na).get;
println("=unapply=name==" + name);
}
}
- For—yield
package com.jinghangzz.scala.test
object ForTest {
def main(args: Array[String]): Unit = {
/*
* 准备了一个容器,此容器中放的是Users
* List[User]
* */
val userBase = List( User("Travis", 28),
User("Kelly", 33),
User("Jennifer", 44),
User("Dennis", 23) );
/* 如何循环List
* 请只查询出名字
* */
for(user <- userBase)
{
println(user + "---循环--" + user.name + "---" + s"${user.name}");
}
/* 返回值就是一个容器;新的容器中只有用户的名字
* if后面可以跟上过滤条件
* */
var newList = for(user <- userBase if(user.age > 30) ) yield user.name ;
/* 超级简洁 */
newList.foreach(println(_))
}
}
/**
* 案例类
*/
case class User(name: String, age: Int)
{
}
- 自己总结
package com.jinghangzz.scala.teabig
/**
* 公共的语法
*/
object Common {
/**
* 一个方法可以传多个参数,
* 在方法的定义处,形参String ... a ;
* eles:Int*表示在调用的时候参数的个数,您随便填写
*/
def multiParam(eles: Int*)
{
/* for循环 */
for(temp <- eles)
{
println("--multiParam--" + temp)
}
}
/**
* 有多个参数列表
* 这有返回值
*/
def keli(a:Int)(b:Int) : Int =
{
a * b ;
}
/* 调用柯里化
* 需要两个参数keli;
* 调用处在写keli2:的时候只用传一个参数,因为第二个参数,已经传过了
* */
def keli2=keli(2)_ ;
/**
* 测试流程相关的内容
* 顺序
* 选择
* 循环
*/
def liucheng()
{
println("===liucheng====");
/* 包含1和5,1-->5 */
println("----Range 1 to 5---" + (1 to 5) );
/* 包含1,不包含5,1-->5 */
println("--Range 1 until 5-----" + (1 until 5) );
for(i <- 1 to 5 )
{
println("=== 1 to 5==" + i );
}
for(i <- 1 until 5 )
{
println("=== 1 until 5==" + i );
}
/* 加条件:for yield */
for(i <- 1 until 5 if i % 2 == 0 )
{
println("=== 1 条件 5==" + i );
}
/* Range 1 until 5=== Range(1,5)*/
println("--Range-----" + Range(1,5) );
}
def main(args: Array[String]): Unit = {
println("==明宣宗--宣德帝---朱瞻基==");
/*---------字符串-------*/
var str = "abcd" +
"fga" ;
/* scala:字符串可以用三个双引号括起来,有嘛好处
* 换行的时候每一行不用加上加号(+)
* 而且在打印的时候原样输出 */
var str1 = """abcd
fg
a""" ;
println("---三个双引号--"+str1);
/* 懒加载变量
* 只有在使用的时候才会执行操作 */
lazy val lazy1 = "abc" ;
println("==lazy1===" + lazy1) ;
println("==lazy1===" + lazy1) ;
/* 调用 */
multiParam(3,4,5,6,7,7,8,8);
/* 调用柯里化函数 */
var res = keli(3)(2);
println("---keli--"+res);
/* 打印keli2 */
println("---keli2--"+keli2(5));
/* 调用方法 */
Common.liucheng();
}
}