1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
      Scala筆記整理(八):類型參數(shù)(泛型)與隱士轉(zhuǎn)換

      [TOC]

      10余年的察哈爾右翼后網(wǎng)站建設(shè)經(jīng)驗,針對設(shè)計、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時及時工作處理。營銷型網(wǎng)站的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動調(diào)整察哈爾右翼后建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計,從而大程度地提升瀏覽體驗。創(chuàng)新互聯(lián)公司從事“察哈爾右翼后網(wǎng)站設(shè)計”,“察哈爾右翼后網(wǎng)站推廣”以來,每個客戶項目都認真落實執(zhí)行。


      概述

      類型參數(shù)是什么?類型參數(shù)其實就是Java中的泛型。大家對Java中的泛型應(yīng)該有所了解,比如我們有List list = new ArrayList(),接著list.add(1),沒問題,list.add("2"),然后我們list.get(1) == 2,對不對?肯定不對了,list.get(1)獲取的其實是個String——"2",String——"2"怎么可能與一個Integer類型的2相等呢?

      所以Java中提出了泛型的概念,其實也就是類型參數(shù)的概念,此時可以用泛型創(chuàng)建List,List list = new ArrayList[Integer](),那么,此時list.add(1)沒問題,而list.add("2")呢?就不行了,因為類型泛型會限制傳入的參數(shù),只能往集合中l(wèi)ist添加Integer類型,這樣就避免了上述的數(shù)值的問題。

      Scala中的類型參數(shù)和Java的泛型是一樣的,也是定義一種類型參數(shù)。

      最后,Scala類型參數(shù)也是Spark源碼中非常常見的,因此同樣必須掌握,才能看懂spark源碼。

      泛型類

      Java 或 C++ 一樣,類和特質(zhì)可以帶類型參數(shù)。在Scala中,我們用方括號類定義類型參數(shù)

      class Student[T, S](val first: T, val second: S)

      以上將定義一個帶有2個類型參數(shù)T和S的類。在類的定義中,你可以用類型參數(shù)來定義變量,方法參數(shù),以及返回值的類型。

      我們把帶有一個或者多個類型參數(shù)的類,叫作泛型類。如果你把類型參數(shù)替換成實際的類型,將得到一個普通的類。比如Student[Int,String]

      Scala會從構(gòu)造參數(shù)中推斷出實際類型:

      val p = new Student(42, "String")

      你也可以自己指定類型,測試代碼如下:

      package cn.xpleaf.bigdata.p5.mygeneric
      
      /**
        * scala的類型參數(shù),即java中的泛型
        * 定義方式有異,java使用使用<>,scala使用[]
        * 泛型可以定義在類 特質(zhì) 方法 函數(shù)
        *     泛型的作用,就是將運行期間的異常,提前到了編譯器
        *     提高代碼的通用性
        */
      object _01GenericOps {
        def main(args: Array[String]): Unit = {
          genericOps1
        }
      
        /**
          * 泛型類的定義
          */
        def genericOps1: Unit = {
          class Student[T, S](val first: T, val second: S) {
            println(first + "\t" + second)
          }
      
          new Student(23, "xpleaf") // 可以做類型的自動推斷
          new Student[Int, String](22, "jieling")
          new Student[Any, Any]("hadoop", "spark")
        }
      }

      輸出結(jié)果如下:

      23  xpleaf
      22  jieling
      hadoop  spark

      泛型函數(shù)

      函數(shù)和方法也可以帶有類型參數(shù):

      def getStudentInfo[T](stu: Array[T]) = stu(stu.length / 2)

      和泛型類一樣,你需要把類型參數(shù)放在方法名后面。

      Scala會從調(diào)用該方法使用的實際類型來推斷出類型:

      def methodOps: Unit ={
          def getStudentInfo[T](stu: Array[T]) = stu(stu.length / 2)
      
          val student = getStudentInfo(Array("garry", "tom", "john", "lucy", "Richard"))
          println(student)
      }

      在main函數(shù)中測試,輸出結(jié)果如下:

      john

      類型變量界定—上限(upper bounds)

      我們先看一個簡單的實例,用于判斷兩個變量中較大的值,其中兩個變量的類型均為Int型

      /**
          * 類型變量界定
          */
      def typeValueOps: Unit ={
          class StudentInt(val first: Int, val second: Int) {
              def bigger = {
                  if (first.compareTo(second) > 0) first else second
              }
          }
      
          val studentInt = new StudentInt(1, 2)
          println(studentInt.bigger)
      }

      上述StudentInt類中的bigger方法調(diào)用了compare方法,如果我們想比較兩個String型的變量的大小,我們可以和上面一樣,添加StudentStr類:

      class StudentStr(val first: String, val second: String) {
          def bigger = {
              if (first.compareTo(second) > 0) first else second
          }
      }

      如果我們針對每種基本類型都寫一個具體的類,則代碼量太大,同時也不夠簡潔,此時我們想到泛型能比較容易解決這個問題:

      class Student[T](val first: T, val second: T) {
          def smaller = if (first.compareTo(second) < 0) first else second
      }

      然而與此同時,我們定義的泛型T并沒有指定實現(xiàn)compareTo方法,也沒有指定為某個類型的子類。在Java泛型里表示某個類型是Test類型的子類型,使用extends關(guān)鍵字:

      
      
      //或用通配符的形式:
      

      這種形式也叫upper bounds (上限或上界),同樣的意思在Scala中的寫法為:

      [T <: Test] //或用通配符: [_ <: Test]

      下面的代碼結(jié)合了上限:

      class Student[T <: Comparable[T]](val first: T, val second: T){
          def smaller = if (first.compareTo(second) < 0) first else second
      }
      
      val studentString = new Student[String]("limu","john")
      println(studentString.smaller)
      
      val studentInt = new Student[Integer](1,2)
      println(studentInt.smaller)

      注意,這相當于是對類型T加了一條限制:T必須是Comparable[T]的子類型。原來給T指定什么類型都可以,現(xiàn)在就不行了。

      這樣一來,我們可以實例化Student[String]。但是不能實例化Student[File],因為String是Comparable[String]的子類型,而File并沒有實現(xiàn)Comparable[File]接口。

      一個包含視圖界定的完整案例如下:

      /**
            * 泛型的上界
            * Upper Bound
            * [T <: 類] ---> [T <% 類] (視圖的界定)
            */
      def genericOps3: Unit = {
          class Student[T <% Comparable[T]](private val first: T, private val second: T) {
              def bigger():T = {
                  /**
                        * 如果要讓first和second有compareTo方法,必須要為Comparable的子類或者是Ordered的子類
                        * 說白了也就是要讓這個類型參數(shù)T是Comparable或者Ordered的子類
                        * 一個類型是某一個類的子類,寫法就要發(fā)生對應(yīng)的變化
                        * java的寫法:
                        * scala的寫法:[T <: Comparable]
                        */
                  if(first.compareTo(second) > 0) {
                      first
                  } else {
                      second
                  }
              }
          }
      
          val stu = new Student[String]("xpleaf", "jieling")
          println(stu.bigger())
          val stu2 = new Student[String]("李四", "王五")
          println(stu2.bigger())
          /**
                * Error:(43, 13) type arguments [Int] do not conform to class Student's type parameter bounds [T <: Comparable[T]]
              val stu3 = new Student[Int](18, 19)
                說明Int不是Comparable的子類
      
                前面Int類型可以用,實際上是scala內(nèi)部,將Int(隱士)轉(zhuǎn)換為RichInt
                要想讓該程序運行通過,就需要使用視圖界定的方式
                [T <% Comparable[T]]
                使用這個%,其實就是強制指定將Int類型隱士轉(zhuǎn)換為RichInt,而RichInt間接實現(xiàn)了Comparable
                */
          val stu3 = new Student[Int](18, 19)
          println(stu3.bigger())
      
      }

      在main函數(shù)中執(zhí)行,輸出結(jié)果如下:

      xpleaf
      王五
      19

      下限很少使用,所以這里就不進行說明了。

      視圖界定

      其實上面已經(jīng)有說明和應(yīng)用,不過這里還是詳細介紹一下。

      剛才將的類型變量界定建立在類繼承層次結(jié)構(gòu)的基礎(chǔ)上,但有時候這種限定不能滿足實際要求,如果希望跨越類繼承層次結(jié)構(gòu)時,可以使用視圖界定來實現(xiàn)的,其后面的原理是通過隱式轉(zhuǎn)換(我們在下一講中會詳細講解什么是隱式轉(zhuǎn)換)來實現(xiàn)。視圖界定利用<%符號來實現(xiàn)。

      先看下面的一個例子:

      class Student[T <: Comparable[T]](val first: T, val second: T) {
          def smaller = if (first.compareTo(second) < 0) first else second
      }
      val student = new Student[Int](4,2)
      println(student.smaller)

      可惜,如果我們嘗試用Student(4,2)五實現(xiàn),編譯器會報錯。因為Int和Integer不一樣,Integer是包裝類型,但是Scala的Int并沒有實現(xiàn)Comparable。
      不過RichInt實現(xiàn)了Comparable[Int],同時還有一個Int到RichInt的隱士轉(zhuǎn)換。解決途徑就是視圖界定。

      class Student[T <% Comparable[T]](val first: T, val second: T) {
          def smaller = if (first.compareTo(second) < 0) first else second
      }

      <%關(guān)系意味著T可以被隱式轉(zhuǎn)換成Comparable[Int]

      個人理解:不管是類型變量界定還是視圖界定,實際上都是在限制類型參數(shù)T,類型變量界定要求類型參數(shù)T必須是上界的子類或者是下界的父類;視圖界定則是要求類型參數(shù)T必須能夠隱式轉(zhuǎn)換成“類似上界”的界定,比如上面提到的,Int隱式轉(zhuǎn)換成RichInt,RichInt是Comparable[Int]的子類。這樣看來,類型變量界定對類型參數(shù)的限制比視圖界定對類型參數(shù)的限制是更大了。

      協(xié)變和逆變

      直接看下面的程序代碼就能很容易理解:

      package cn.xpleaf.bigdata.p5.mygeneric
      
      /**
        * scala類型參數(shù)的協(xié)變和逆變
        *     scala默認不支持協(xié)變和逆變
        *         要想讓scala的泛型支持協(xié)變,在泛型前面再加一個"+"
        *         要想讓scala的泛型支持逆變,在泛型前面再加一個"-"
        *     但是一個類不能同時支持協(xié)變和逆變
        */
      object _02GenericOps {
          def main(args: Array[String]): Unit = {
              /*
              val list:List[Person] = List[Person]()  // 正常的定義
      
              val list1:List[Person] = List[Student]()    // scala中的協(xié)變,java不支持
              // val list2:List[Teacher] = List[Person]()    // 逆變,java不支持,但是scala需要在定義泛型類的時候指定
              */
      
              val myList1:MyList[Person] = new MyList[Person]()
              val myList2:MyList[Person] = new MyList[Student]()
      
              val yourList1:YourList[Person] = new YourList[Person]()
              val yourList2:YourList[Student] = new YourList[Person]()
          }
      
          class Person{}
          class Student extends Person{}
          class Teacher extends Person{}
      
          /**
            * 支持協(xié)變的泛型類
            */
          class MyList[+T] {
      
          }
      
          /**
            * 支持逆變的泛型類
            */
          class YourList[-T] {
      
          }
      
      }

      當然還有很多的理論知識和細節(jié)知識,但目前掌握這些就可以了。

      類型通配符

      1、類型通配符是指在使用時不具體指定它屬于某個類,而是只知道其大致的類型范圍,通過”_
      <:” 達到類型通配的目的。

      2、

      def typeWildcard: Unit ={
          class Person(val name:String){
              override def toString()=name
          }
          class Student(name:String) extends Person(name)
          class Teacher(name:String) extends Person(name)
          class Pair[T](val first:T,val second:T){
              override def toString()="first:"+first+", second: "+second;
          }
          //Pair的類型參數(shù)限定為[_<:Person],即輸入的類為Person及其子類
          //類型通配符和一般的泛型定義不一樣,泛型在類定義時使用,而類型通配符號在使用類時使用
          def makeFriends(p:Pair[_<:Person])={
              println(p.first +" is making friend with "+ p.second)
          }
          makeFriends(new Pair(new Student("john"),new Teacher("搖擺少年夢")))
      }

      隱士轉(zhuǎn)換

      概述

      1、在scala語言當中,隱式轉(zhuǎn)換是一項強大的程序語言功能,它不僅能夠簡化程序設(shè)計,也能夠使程序具有很強的靈活性。它們存在固有的隱式轉(zhuǎn)換,不需要人工進行干預(yù),例如Float在必要情況下自動轉(zhuǎn)換為Double類型

      2、在前一講的視圖界定中我們也提到,視圖界定可以跨越類層次結(jié)構(gòu)進行,它背后的實現(xiàn)原理就是隱式轉(zhuǎn)換,例如Int類型會視圖界定中會自動轉(zhuǎn)換成RichInt,而RichInt實現(xiàn)了Comparable接口,當然這里面的隱式轉(zhuǎn)換也是scala語言為我們設(shè)計好的 。

      3、所謂隱士轉(zhuǎn)換函數(shù)(implicit conversion function)指的是那種以implicit關(guān)鍵字聲明的帶有單個參數(shù)的函數(shù)。正如它的名稱所表達的,這樣的函數(shù)將自動應(yīng)用,將值從一種類型轉(zhuǎn)換成另一種類型。

      Doube進行到Int的轉(zhuǎn)換:

      val x:Int = 3.5
      implicit def double2Int(x:Double)=x.toInt
      def conversionFunc: Unit ={
          //Doube進行到Int的轉(zhuǎn)換
          val x:Int = 3.5
          println("x===> " + x)
      }

      1、隱式函數(shù)的名稱對結(jié)構(gòu)沒有影響,即implicitdefdouble2Int(x:Double)=x.toInt函數(shù)可以是任何名字,只不過采用source2Target這種方式函數(shù)的意思比較明確,閱讀代碼的人可以見名知義,增加代碼的可讀性。

      2、Scala并不是第一個允許程序員提供自動類型轉(zhuǎn)換的語言。不過,Scala給了程序員相當大的控制權(quán)在什么時候應(yīng)用這些模塊。

      利用隱士函數(shù)豐富現(xiàn)在類庫的功能

      隱式轉(zhuǎn)換功能十分強大,可以快速地擴展現(xiàn)有類庫的功能.

      import java.io.File
      import scala.io.Source
      
      //RichFile類中定義了Read方法
      class RichFile(val file:File){
          def read = Source.fromFile(file).getLines().mkString
      }
      
      //隱式函數(shù)將java.io.File隱式轉(zhuǎn)換為RichFile類
      implicit def file2RichFile(file:File) = new RichFile(file)
      val f = new File("E:/test/scala/wordcount.txt").read
      println(f)

      Java.io.File本身并沒有read方法。

      引入隱士轉(zhuǎn)換

      1、Scala默認會考慮兩種隱式轉(zhuǎn)換,一種是源類型,或者目標類型的伴生對象內(nèi)的隱式轉(zhuǎn)換函數(shù);一種是當前程序作用域內(nèi)的可以用唯一標識符表示的隱式轉(zhuǎn)換函數(shù)。

      2、如果隱式轉(zhuǎn)換不在上述兩種情況下的話,那么就必須手動使用import語法引入某個包下的隱式轉(zhuǎn)換函數(shù),比如import student._。

      通常建議,僅僅在需要進行隱式轉(zhuǎn)換的代碼部分,比如某個函數(shù)或者方法內(nèi),用import導(dǎo)入隱式轉(zhuǎn)換函數(shù),這樣可以縮小隱式轉(zhuǎn)換函數(shù)的作用域,避免不需要的隱式轉(zhuǎn)換。

      隱士轉(zhuǎn)換規(guī)則

      1、隱式轉(zhuǎn)換可以定義在目標文件當中(一個Scala文件中)

      //轉(zhuǎn)換函數(shù)
      implicit def double2Int(x:Double)=x.toInt
      val x:Int = 3.5

      2、隱式轉(zhuǎn)換函數(shù)與目標代碼在同一個文件當中,也可以將隱式轉(zhuǎn)換集中放置在某個包中,在使用進直接將該包引入即可

      //在com.sparkstudy.scala.demo包中定義了子包implicitConversion
      //然后在object ImplicitConversion中定義所有的引式轉(zhuǎn)換方法
      package implicitConversion{
          object ImplicitConversion{
              implicit def double2Int(x:Double)=x.toInt
              implicit def file2RichFile(file:File) = new RichFile(file)
          }
      }
      class RichFile(val file:File){
          def read=Source.fromFile(file).getLines().mkString
      }
      
      //隱士轉(zhuǎn)換規(guī)則
      def implicitConversionRuleOps: Unit ={
          //在使用時引入所有的隱式方法
          import com.sparkstudy.scala.demo.implicitConversion.ImplicitConversion._
          var x:Int=3.5
          println("x===> " + x)
          val f=new File("E:/test/scala/wordcount.txt").read
          println(f)
      }

      這種方式在scala語言中比較常見,在前面我們也提到,scala會默認幫我們引用Predef對象中所有的方法,Predef中定義了很多隱式轉(zhuǎn)換函數(shù)

      隱士轉(zhuǎn)換發(fā)生的時機

      1、當方法中參數(shù)的類型與實際類型不一致時

      def f(x:Int)=x
      //方法中輸入的參數(shù)類型與實際類型不一致,此時會發(fā)生隱式轉(zhuǎn)換
      //double類型會轉(zhuǎn)換為Int類型,再進行方法的執(zhí)行
      f(3.14)

      2、當調(diào)用類中不存在的方法或成員時,會自動將對象進行隱式轉(zhuǎn)換

      我們上面進行的那個案例(File本身是沒有read方法)

      隱士參數(shù)

      1、所謂的隱式參數(shù),指的是在函數(shù)或者方法中,定義一個用implicit修飾的參數(shù),此時Scala會嘗試找到一個指定類型的,用implicit修飾的對象,即隱式值,并注入?yún)?shù)。

      2、Scala會在兩個范圍內(nèi)查找:一種是當前作用域內(nèi)可見的val或var定義的隱式變量;一種是隱式參數(shù)類型的伴生對象內(nèi)的隱式值

      //學(xué)生畢業(yè)報告
      class StudentSubmitReport {
          def writeReport(ctent: String) = println(ctent)
      }
      
      implicit val stuentSign = new StudentSubmitReport
      
      def signForReport(name: String) (implicit studentSReport: StudentSubmitReport) {
          studentSReport.writeReport(name + "come to here")
      }
      signForReport ("jack")

      完整案例

      ImplicitUtil
      package cn.xpleaf.bigdata.p5
      
      import java.io.File
      
      import scala.io.Source
      
      object ImplicitUtil {
      
          implicit def double2Int(d: Double): Int = d.toInt
      
          implicit def str2Int(str: String): Int = str.length
      
          implicit def file2RichFile(file: File) = new RichFile(file)
      
          implicit val swr:StudentWriteReport = new StudentWriteReport()
      }
      
      class RichFile(file: File) {
          def read() = Source.fromFile(file).getLines().mkString
      }
      
      class StudentWriteReport {
          def writeReport(content:String) = println(content)
      }
      implicitOps
      package cn.xpleaf.bigdata.p5.implicitz
      
      /**
        * scala隱士轉(zhuǎn)換操作
        *     將一種類型,轉(zhuǎn)化為另外的一種類型,這完成這一操作的背后就是隱士轉(zhuǎn)換函數(shù)
        *     所謂隱士轉(zhuǎn)換函數(shù),其實就是在普通函數(shù)前面加上一個關(guān)鍵字——implicit
        *
        *     隱士轉(zhuǎn)換函數(shù)的導(dǎo)入:
        *         1、如果隱士轉(zhuǎn)換函數(shù)和調(diào)用它的操作,在同一個文件中,我們不要做任何操作
        *         2、如果不在一個文件中,需要收到導(dǎo)入,和導(dǎo)包是一樣,唯一需要注意最后以._結(jié)尾,表導(dǎo)入該類中的所有的隱士轉(zhuǎn)換函數(shù)
        *
        */
      import java.io.File
      
      import cn.xpleaf.bigdata.p5.ImplicitUtil._
      import cn.xpleaf.bigdata.p5.StudentWriteReport
      
      import scala.io.Source
      object implicitOps {
          def main(args: Array[String]): Unit = {
              //        implicitOps1
              //        implicitOps2
              implicitOps3
          }
      
          /**
            * 隱士轉(zhuǎn)換參數(shù)
            * 其實就非常類似于之前學(xué)習(xí)過的柯里化
            */
          def implicitOps3: Unit = {
              /*  // 傳統(tǒng)操作方式
              def signReport(name:String, swr:StudentWriteReport): Unit = {
                  swr.writeReport(name)
              }
      
              signReport("張三", new StudentWriteReport())*/
      
              def signForReport(name:String)(implicit swr:StudentWriteReport): Unit = {
                  swr.writeReport(name)
              }
      
              signForReport("張三")
          }
      
          /*
          class StudentWriteReport {
              def writeReport(content:String) = println(content)
          }
      
          implicit val swr:StudentWriteReport = new StudentWriteReport()
          */
      
          /**
            * 使用隱士轉(zhuǎn)換豐富現(xiàn)在類型的API
            */
          def implicitOps2: Unit ={
      
              var file = new File("/Users/yeyonghao/test.txt")
      
              var lines = file.read()
      
              println(lines)
          }
      
          /**
            * 隱士轉(zhuǎn)換操作
            */
          def implicitOps1: Unit = {
              val x:Int = 3
              val y:Int = 3.5
              val z:Int = "klkelfldlkfj"
      
              println("x=" + x)
              println("y=" + y)
              println("z=" + z)
      
          }
      
      }

      新聞名稱:Scala筆記整理(八):類型參數(shù)(泛型)與隱士轉(zhuǎn)換
      網(wǎng)頁網(wǎng)址:http://www.ef60e0e.cn/article/ieeced.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        洛南县| 沂南县| 山西省| 比如县| 桃源县| 虹口区| 抚州市| 宁国市| 南漳县| 新宁县| 葫芦岛市| 华容县| 鹤岗市| 阜新市| 文安县| 南丰县| 嘉祥县| 二手房| 仙桃市| 阿城市| 宿州市| 金沙县| 安仁县| 西城区| 万源市| 天台县| 沧源| 手游| 宿迁市| 大田县| 中西区| 泰州市| 酒泉市| 五华县| 博爱县| 迭部县| 博白县| 南丰县| 石河子市| 敦化市| 西华县|