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 的源文件中。
    每个包都允许有一个包对象。 在包对象中的任何定义都被认为是包自身的成员
    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();
  }
}