zoukankan      html  css  js  c++  java
  • Scala语法入门

    Scala是一门多范式的编程语言,静态类型、整合面向对象和函数式语言的功能。

    Scala被编译后在Java虚拟机上运行,兼容Java语言。

    一、Hello World

    Scala运行在JVM上,需要有Java的支持,可以在交互模式下运行。

    0、Hello World

    1、直接在命令行下输入scala,就可以进入scala的交互模式,默认会输出Java和Scala版本。

    C:Usersscala>scala  
    Welcome to Scala 2.13.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_191).
    Type in expressions for evaluation. Or try :help.
    
    //打印
    scala> println("Hello World")
    Hello World
    
    

    2、建立一个Hello.scala的文件,写入代码,然后使用scalacscala编译运行即可,和Java的编译方式一样。

    object Hello {
      def main(args: Array[String]): Unit = {
        println("hello")
    
      }
    }
    
    F:scala>scalac Hello.scala
    
    F:scala>scala Hello
    hello
    
    //文件夹下会有三个文件,源文件、字节码文件、$文件(scala没有静态方法)
    Hello$.class
    Hello.class
    Hello.scala
    

    scala中没有静态方法,执行入口在单例对象中,main方法。

    Javascala中可以使用,但有时需要做一定的转换。

    object Hello {
      def main(args: Array[String]): Unit = {
        System.out.println("Hello World") //兼容Java语言
      }
    }
    
    

    1、基础语句

    基础的打印语句、变量定义。

     def main(args: Array[String]): Unit = {
    
        println("Hello World") //打印
        
        val a :Int = 10
        printf("Int型变量a: %d
     ",a)
    
        //尽量使用val修饰
        var str = "Scala" //类型推断
    
        print("var字符串变量: "+str)
    
      }
    
    print等函数用于打印。
    
    val、var用于定义变量,定义方法为:
    	val var_name :type --type不写程序会自动推断
    
    Scala 的变量分为两种,val 和 var,其区别如下:
        val : 类似于 Java 中的 final 变量,一旦初始化就不能被重新赋值;
        var :类似于 Java 中的非 final 变量,在整个声明周期内 var 可以被重新赋值;
    
    

    2 、数据类型

    Scala与Java具有相同的数据类型,具有相同的内存占用和精度。以下是提供Scala中可用的所有数据类型的详细信息的表格:

    序号 数据类型 说明
    1 Byte 8位有符号值,范围从-128127
    2 Short 16位有符号值,范围从-3276832767
    3 Int 32位有符号值,范围从-21474836482147483647
    4 Long 64位有符号值,范围从-92233720368547758089223372036854775807
    5 Float 32位IEEE 754单精度浮点值
    6 Double 64位IEEE 754双精度浮点值
    7 Char 16位无符号Unicode字符。范围从U+0000U+FFFF
    8 String 一个Char类型序列
    9 Boolean 文字值true或文字值false
    10 Unit 对应于无值
    11 Null null或空引用
    12 Nothing 每种其他类型的亚型; 不包括无值
    13 Any 任何类型的超类型; 任何对象的类型为Any
    14 AnyRef 任何引用类型的超类型

    上面列出的所有数据类型都是对象。Scala中没有类似Java中那样的原始类型。 这意味着您可以调用IntLong等方法。

    连运算符都是方法,+==.+()。(Scala 的面向对象比 Java 更加纯粹,在 Scala 中一切都是对象)

        val a :Int = 10
    
        println(a+10)//20
        println(a.+(10))//20
    
        println(a*2)
        println(a.*(2))
        
    

    字符串是支持插值的。

        val what = new String("Nice")
        val name :String="Scala"
    
        println(s"$name 字符串插值 ${what+name}") 
    

    scala中的字符串就是java中的字符串

    3、流程、循环

    00、if、else

    scala中的if elsejava唯一的区别在于是有返回值的。

        val num :Int = 10
    
        val res = if( num !=1) { //接收返回值
             "no"
        }
    
    
        println(res)//no
    

    scala中的语句块通常最后一个语句及return的值,如果有返回值的话。

    01、块表达式

    使用{}将一堆语句包含成一个块,当然也可以有返回值。

        val result = {
          val a = 1 + 1; val b = 2 + 2; a + b
        }
        print(result)//6
    
    02、while、do ...while

    这种循环大多数语言中都含有,这里和Java一样。(没有返回值,循环怎么返回值,for循环可以?)

        var i = 10
        while(i>1){
    
          i-=1
    
        }
    
        println(i)//1
    
    03、for循环

    scala中的for循环和Java中相差的就比较大了,使用<-赋值,不需要使用val/var声明,并且还可以加入条件过滤、多表达式、返回值。

     //集合[1,10]
        for (i <- 1 to 10){ 
          print(i)
        } //12345678910
        
        
    
        //集合[1,9)
        for (i <- 1 until 10){
          print(i)
        }//123456789
    
        
    
        //条件过滤
        for(i <- 1.to(10) if i%2==1){
          print(i)
        }//13579
    
    
    
    
     	//多表达式生成器,相当于多层for循环
        for(i <- 1 to 3;j <- 1 until 3){
          print(i,j)
        }//(1,1)(1,2)(2,1)(2,2)(3,1)(3,2)
    
    	打印语句接收多参数时,输出(),换个写法
    	print(s"$i,$j ")//1,1 1,2 2,1 2,2 3,1 3,2
    
    
     //返回一个Vector(10, 20, 30, 40, 50, 60, 70, 80, 90, 100),整个循环作为一个表达式时可用yield
        val res = for (i <- 1 to 10) yield i*10
    
        println(res)
    
    

    until、to都是Int变量的方法,i <- 1 to 3也可以写成 i <- 1.to(3).

    函数的不同写法,在函数式编程中解释。

    04、match、case

    match类似switch,但是与 Java 中的 switch 有以下三点不同:

    • Scala 中的 case 语句支持任何类型;而 Java 中 case 语句仅支持整型、枚举和字符串常量;
    • Scala 中每个分支语句后面不需要写 break,因为在 case 语句中 break 是隐含的,默认就有;
    • 在 Scala 中 match 语句是有返回值的,而 Java 中 switch 语句是没有返回值的。如下:
    	val e= StdIn.readLine("输入:") //从控制台输入
        println(e)
        val res = e match {
                  case "A" => println(10)
                  case "B" => println(20)
                  case "C" => println(30)
                  case _ => "no found "
        }
    
        println("res== "+res)
    
    
    输入:G
    G
    res== no found 
    
    
    输入:C
    C
    30
    res== ()
    

    => 和Java中的->有点像,函数式编程中使用,指向函数体。

    _在scala中指代任意值,常用于占位

    4、方法、函数

    scala是一门多范式的编程语言,函数和方法都是对功能的封装,在Java里函数就是方法,但在scala中不能混为一谈。

    1、scala是极端的面向对象,所以函数也是对象,函数拥有自己的方法(apply、toString等)

    2、函数是一等公民,可以在任意位置出现(作为参数),方法不可以

    3、函数和方法可以互相转换。

    4、函数和方法有多种表达,前后缀表达(无参时省略括号,如println打印空行 、使用空格函参分离,如1 to 10等)

    00、定义

    方法需要使用def=关键字定义, 需要指定返回值类型。

    函数使用=>作为标识,def关键字定义,但也可以使用变量接收匿名函数(def相当于被预先定义),无需指定返回类型,默认返回语句块末行。

    object FAndM {
    
      //方法
      def sayHi(name :String):Unit ={
        println(s"$name Say Hi")
      }
    
      //函数,匿名函数赋值给变量sayHello
      val sayHello = () => {
        println("Hello")
    
      }
        
       //函数:使用def定义
       def multi = (x: Int) => {x * x}
    
      //函数作为参数
      def f(ff :Function0[Unit]) ={
          ff()
    
      }
      
      def main(args: Array[String]): Unit = {
        println("函数类型: "+sayHello) //<function0> ,函数类型是funcationN
    
    
        f(sayHello) //将函数作为参数给方法
    
    
        f(()=>{
          println("匿名函数")
        })
          
        val fM =f _ //空格_ 可以实现转换
        fM(()=>{
          println("方法转换为函数")
        })
    
      }
    }
    
    
    01、参数

    方法可以使用不定参数,使用String*表示,使用默认参数值。

      def main(args: Array[String]): Unit = {
           	echoM("1-","2-","3-") //WrappedArray(1-, 2-, 3-),其实也是个集合
        	sayHello() //hello Scala
    
      }
    
    //方法、不定参数
      def echoM(args:String*): Unit ={
        println(args)
      }
    
    //方法、默认参数值
      def sayHello(name :String ="Scala" ) ={
        println(s"hello $name")
      }
    

    在函数中、这些似乎都不适用。

    03、类型

    函数和方法在scala里面是不同的,方法属于对象,而函数是对象。可以说函数是方法的上层。

    方法的类型是().

    函数的上层是FunctionN,这里N代表函数参数数量。

    println(echoM("1","2"))   // 方法类型是()
    println(sayHello()) //()
    
    
    val f1: String => Unit =echoF   //函数类型是String => Unit
    val f2: Function1[String,Unit] = echoF  //可以使用上层接收,Function1[String,Unit],这也是泛型
    
    04、闭包

    在函数式语言中经常有闭包的概念:简单来说就是函数中能访问、甚至修改到外界的变量,相当于改变了变量的作用域。

    	var num =100 //外界变量
    
      //函数
      def sum=(n:Int) => {
        num+=n
        println(num)
      }
    
    
    
      def main(args: Array[String]): Unit = {
    
        sum(10)//110
        sum(10)//120
    
      }
    

    Java中的表达是这样的。

    public class Hello {
    
        int num=100;
    
        public void sum(int x){
            num+=x;
            System.out.println(num);
        }
    
        public static void main(String[] args) {
            Hello hello = new Hello();
            hello.sum(10); //110
            hello.sum(10);//120
    
        }
    }
    
    
    05、柯里化

    柯里化指的是将原来接受两个(N个)参数的函数变成接受一个参数的函数的过程。新的函数以原有第二个参数作为参数。

    def plus(x:Int,y:Int) => x+y
    变为:def plus(x:Int)=(y:Int)=>x+y

    柯里化也是一种闭包。

    柯里化可以使最后一个函数参数改变,使整个函数的功能发生变化。

    def sum(x:Int,y:Int)={
        x+y
      }
      
      //柯里化
      def sum2(x:Int)(y:Int)={
        x+y
      }
    //柯里化,第二个参数是函数,第二个参数改变,整个函数功能改变
      def sum3(x:Int)(f:(Int)=>Int)={
        f(x)
      }
    
    
      def main(args: Array[String]): Unit = {
    
        println(sum(1,2))
    
        println(sum2(1)(2))
        println(sum3(1)((k)=>k+3)) //传一个匿名函数
        println(sum3(1)((k)=>k*1)) //sum3的功能变化了
      }
    
    

    接收多少参数都可以。

    def sumN (x0:Int)(x1:Int)(x2:Int)(x3:Int)(f:(Int*)=>Unit)={
        f(x0,x1,x2,x3)
      }
    
      def main(args: Array[String]): Unit = {
        sumN(0)(1)(2)(3)(k=>k.foreach(println))
        
      }
    
    

    5、数据结构

    00、数组、变长数组

    基础结构数组,scala中的数组索引使用()获取,而不是[].

    因为获取和修改实际上是调用了数组的以下两个方法。

    apply
    update
    
    

    数组的建立方式和遍历方式。

    	
    	//1、声明容量创建数组
    	val arr: Array[Int] = new Array[Int](10)
        arr(0)=100  //arr.update(0,100)
        arr(1)=1
        arr(2)=2
        arr(3)=3
        arr(4)=4
    
        for (e <- arr) { //循环获取元素遍历
          print(e) //100123400000
        }
    
    
    
    //2、直接接收数组(有点像强转,不过apply方法可以帮助对象创建时省去new关键字)
       val arr2 = Array(1,2,3,4,5)
    
        for (index <- arr2.indices){ //获取索引,遍历
          print(arr2(index)) //12345
        }
    
    

    变长数组,可以伸缩,无需指定容量,和普通数组可以互相转换。

    	val arr = Array(1, 2, 3, 4, 5)
    
        //缓冲数组,可变长
        val ab = ArrayBuffer(9, 8, 7, 6)
    
        ab+=10   //追加一个元素
        ab++=arr //追加一个数组
    
        //遍历(函数式)
        ab.foreach(print) //1234112345
    
        println //无参,省略括号
    
        val array: Array[Int] = ab.toArray //缓存转普通数组
        val abb: mutable.Buffer[Int] =arr.toBuffer //普通数组转缓冲
    
    

    scalajava的集合是可以互相转换的(不同版本sdk的api还不一样。。。。)

      	//scala 2.11.X
    	val unit = JavaConverters.bufferAsJavaListConverter(ab)
        val java: util.List[Int] = unit.asJava
    
    
    	//scala 2.12.x
        val element = ArrayBuffer("java", "scala", "array")
        // Scala 转 Java
        val javaList: util.List[String] = JavaConverters.bufferAsJavaList(element)
    
    
    01、迭代器

    scala中的迭代器和java类似,用于迭代集合元素,位于大多数集合的上层

    	val it = arr2.iterator //无参,省略括号
    
        while(it.hasNext){
          print(it.next())
        }
    
    
    02、List、Set、Map、Tuple

    Scala 中拥有多种集合类型,主要分为可变的和不可变的集合两大类:

    • 可变集合: 可以被修改。即可以更改,添加,删除集合中的元素;
    • 不可变集合类:不能被修改。对集合执行更改,添加或删除操作都会返回一个新的集合,而不是修改原来的集合。

    Scala 中的大部分集合类都存在三类变体,分别位于 scala.collection, scala.collection.immutable, scala.collection.mutable 包中。还有部分集合类位于 scala.collection.generic 包下。

    • scala.collection.immutable :包是中的集合是不可变的;
    • scala.collection.mutable :包中的集合是可变的;
    • scala.collection :包中的集合,既可以是可变的,也可以是不可变的。

    collection

    collection

    mutable

    mutable

    immutable

    immutable

    有些集合在不同包下可变、不可变,未申明则创建的都是不可变集合。

    	val list =List("Java","Scala","List") //List List(Java, Scala, List)
    
        list(0)="ok" //error,List不可变
    
        val mlist = mutable.LinkedList("nice",2,3) 
        mlist(0)="可变List"
        println(mlist) //LinkedList(可变List, 2, 3)
    
    

    遍历方式,集合基本都类似。

    	val list =List("Java","Scala","List")
    
    
    	//1、取元素
        for(e<-list) print(e) 
    	println 
    	
    	//2、取索引
        for(e<- list.indices) print(list(e))
    	println
        
    	//3、取迭代器
        val it =list.iterator
        while(it.hasNext)print(it.next)
    	println
        
    	//4、函数式,流式处理
        list foreach print // == list.foreach(println)
    
    

    Set不具备通过索引获取元素(数学中的Set是无序的,那么也谈不上索引)。

     	val set = Set(1,3,"Jim")
        //set无序,存在返回true false
        println(set("Jim"))  //true  ==set.contains("Jim")
        println(set(2))     //false
        println(set(1))  //true
    
    

    Map 未指明使用的都是HashMap.

    	// 从指定的值初始化 Map(方式一)
        val map0 = Map("hadoop" -> 10, "spark" -> 20, "storm" -> 30) 
    
        println(map0)//Map(hadoop -> 10, spark -> 20, storm -> 30)
    
        // 从指定的值初始化 Map(方式二)
        val map1 = Map(("hadoop", 10), ("spark", 20), ("storm", 30))
    
        println(map1) //Map(hadoop -> 10, spark -> 20, storm -> 30)
    
        println(map0("hadoop")) //通过key获取value
    
    
    	//通过key遍历val,for遍历也是
        map0.keys.foreach(k=>{
          println(map0(k))
        })
    	//通过val遍历 MapLike(10, 20, 30)
        map0.values.foreach(println)
    
    

    Tuple(元组)和数组类似,但数组只能存一种类型,tuple没有限制。

        val arr: Array[Any] =Array(1,"ok") //存放不同类型的数据,会被识别为Any
        
    
        val tur: (Int, String) = Tuple2(1,"ok")
        val tur2: (Int, String) = (1,"ok")
    
    

    元组无法遍历,但可以被模式匹配。

        val tur = (1,"Spark",1.10f)
    
        println(tur) //(1,Spark,1.1)
        println(tur._3) //获取元素 1.1
    
        val (a,b,c) =tur //模式匹配
        println(b) //Spark
    
    
    03、泛型

    scala的泛型使用[]声明,没有显式声明时我们也应该知道Any和其他类型的区别。

        val arr: Array[Any] =Array(1,"ok") //存放不同类型的数据,会被识别为Any
        val arr2: Array[Int] =Array(1,2) //Int
    
    

    6、隐式转换、参数

    隐式转换指的是以 implicit 关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能。

    有点像装饰器模式或适配器模式。

    直接在类上定义隐式转换。

    object ImplicitDemo {
    
      // 普通人
      class Person(val name: String)
    
      // 雷神,在这里隐式转换
      implicit class Thor(val person: Person) {
        // 正常情况下只有雷神才能举起雷神之锤
         def hammer(): Unit = {
          println(person.name + "举起雷神之锤")
        }
      }
    
    
    
      def main(args: Array[String]): Unit = {
          val p =new Person("Tom")
          
          p.hammer()
      }
    }
    
    
    

    使用时才转换。

    object ImplicitDemo {
    
      // 普通人
      class Person(val name: String)
    
      // 雷神
      class Thor(val person: Person) {
        // 正常情况下只有雷神才能举起雷神之锤
         def hammer(): Unit = {
          println(person.name + "举起雷神之锤")
        }
      }
    
    
    
      def main(args: Array[String]): Unit = {
        
          //在这里隐式转换
          implicit def pTot(p:Person) =new Thor(p)
    
          val person =new Person("Tom")
          person.hammer()
      }
    }
    
    
    

    二、函数式编程

    关于函数式编程的特点:

    没有副作用,不依赖于外部的数据,而且也不改变外部数据的值,而是返回一个新的值给你

    ​ 传入的参数直接被拷贝一份进行操作,不会对原来的数据进行任何变化。每个数据都是val

    把函数当成变量来用,函数间通过参数和返回值来传递数据,函数里没有临时变量

    ​ 函数可以被当作参数传到下一个函数中,没有临时变量。

    代码是在描述要干什么,而不是怎么干

    ​ 比如要遍历数组,直接使用具有遍历功能的foreach函数,而不是写个for循环,在里面描述如何遍历。

    可以是Pipeline, 让每个功能就做一件事,并把这件事做到极致,使用时直接拼装

    ​ 对不规则数据处理时,需要过滤、修剪、映射、收集等操作,每个功能实现一个函数,然后拼装起来。

    这样非常适合处理数据。

    优点: 间接明了,代码量少。

    缺点:有时太简洁了,看不到细节。

        val list  = List(1,2,3,4,5,6,8,9,10)
    
        //直接使用高阶函数map,对数据进行映射处理,而不是for(k <- list) k+","(这里要求list为var,那么原list会被改变)
         val toList = list.map(k=>k+"---").toArray
    
        //print函数作为参数传递,使用foreach遍历函数而不是for循环
        toList.foreach(print) //1---2---3---4---5---6---8---9---10---
    
        println(list==toList) //false,不会改变原数据,没有副作用
    
    
    高阶函数

    正常的一个函数只能接受常量或者普通变量作为参数,但是高阶函数可以接受函数作为参数,同时高阶函数的返回值也可以是函数。比如常见的有map,filter,reduce等函数,它们可以接受一个函数作为参数。

    常用高阶函数如下:

    很多都是对几何数据进行操作。

    遍历
    xs foreach f 为 xs 的每个元素执行函数 f
    Maps:
    xs map f 对 xs 中每一个元素应用函数 f,并返回一个新的集合
    xs flatMap f 对 xs 中每一个元素应用函数 f,最后将结果合并成一个新的集合
    xs collect f 对 xs 中每一个元素调用偏函数 f,并返回一个新的集合
    Subcollection:
    xs.tail 除了第一个元素之外的其他元素组成的集合
    xs.init 除了最后一个元素之外的其他元素组成的集合
    xs slice (from, to) 返回给定索引范围之内的元素组成的集合 (包含 from 位置的元素但不包含 to 位置的元素)
    xs take n 返回 xs 的前 n 个元素组成的集合(如果无序,则返回任意 n 个元素)
    xs drop n 返回 xs 的后 n 个元素组成的集合(如果无序,则返回任意 n 个元素)
    xs filter p 返回满足条件 p 的所有元素的集合
    案例一、词语处理

    获取字符并转大写。

    Scala中的函数式编程。

        //把这个数组中的单词全部拆出来变成大写
        val data = Array("hello  world","Scala Java","Spark,Scala")
    
        //流式处理
        val array = data.flatMap(k=>k.split("\W+")).filter(k=>k.length>0).map(k=>k.toUpperCase).toArray[String]
    
        array.foreach(println)
    
    

    Java8也提供了流式编程,不过相比scala还是有点繁琐。

    Java8提供的Stream流函数式编程。

     //把这个数组中的单词全部拆出来变成大写
            String[] data = new String[]{"hello  world","Scala Java","Spark,Scala"};
    
            //转换成Stream
            Stream<String> stream = Stream.of(data);
    
            //链式操作,收集数据
    ArrayList<String> list = stream.flatMap(k -> Stream.of(k.split("\W+"))).map(String::toUpperCase).collect(Collectors.toCollection(ArrayList<String>::new));
            System.out.println(list); //[HELLO, WORLD, SCALA, JAVA, SPARK, SCALA]
        }
    
    
    案例二、WordCount、TopKey

    读取文件,找出单词(转大写)出现次数,并排序,获取TopK数据。

    scala语言

     def main(args: Array[String]): Unit = {
    
        //读取文件
       val source: BufferedSource = Source.fromFile("dir/wordcount.txt")
    
        /*
           hadoop Spark hive
          Spark Flink hadoop
          java scala hadoop
          Spark Hadoop Java
        */
        val text:String =source.mkString
        
        //切分字符串为数组
        val strings: Array[String] = text.split("\W+")
    
        //处理数据
        strings.map(_.toUpperCase).map((_,1)).groupBy(_._1).map(k=>(k._1,k._2.length)). //转大写->转元组->分组->聚合
          toArray.sortBy(_._2).reverse                       //排序->反转->遍历
          .foreach(println)
        
        /*
        (HADOOP,4)
        (SPARK,3)
        (JAVA,2)
        (HIVE,1)
        (SCALA,1)
        (FLINK,1)
         */
        source.close()
    
      }
    
    

    Java语言

    Java中的集合要转换为Stream才支持高阶函数。

            FileReader reader = new FileReader(new File("dir/wordcount.txt"));
            char[] chars=new char[1024];
            int len = reader.read(chars);
    
            Stream<String> stream = Stream.of(new String(chars).split("\W+"));//分割字符串并转流
    
            java.util.Map<String, Long> collect = stream.map(String::toUpperCase).collect(Collectors.groupingBy(s->s,Collectors.counting())); //转大写->分类->聚合
    		
    		//wordCount完成
            System.out.println(collect); //{JAVA=3, HIVE=1, HADOOP=4, SCALA=1, HDFS=1, SPARK=3, HBASE=1, YARN=1, FLINK=1}
    
            reader.close();
    
    
    		//排序
            List<java.util.Map.Entry<String, Long>> entryList = collect.entrySet().stream().sorted((e1, e2) -> Long.compare(e1.getValue(), e2.getValue()))
                    .collect(Collectors.toList());
    
            Collections.reverse(entryList);//翻转链表
    		
    		//获取Top5数据,TopK完成
            entryList.stream().limit(5).forEach(System.out::println);
            /*
           HADOOP=4
            SPARK=3
            JAVA=3
            FLINK=1
            YARN=1
             */
    
    

    三、面向对象

    Scala是面向对象的语言,相比于java更加严格。

    一切都是对象,函数和基本数据类型都是对象。运算符+、-、*都是基本类型的方法,使用时Intx+y相当于x.+(y)

    1、Java中的静态方法、字段在scala中被剥离出来放到单例对象、伴生对象中,让类没有入口函数。

    2、单例对象使用object申明,没有对应的class类,可以直接定义main方法或继承App类,作为程序入口直接运行。

    伴生对象是指和类同名的对象(必须在同文件中),和单例对象相反,用于替代静态成员。

    3、scala中没有接口,但是有trait特征,实现同样效果。

    单例对象

    默认是懒汉式单例

    //继承App类,相当于类中的内容都在main函数中
    object App01 extends App {
    
      //类,类名后面跟着主构造器
      class Animal(name:String){}
    
      //样例类,相当于Java中的POJO,默认重写了toString等方法
      case class Person(name:String,age:Int=18){}
    
    
      //使用new关键字新建对象
      println(new Animal("Dave")) //obj.App01$Animal@22a67b4
    
      //样例类默认实现了apply方法,省略new
      println(Person("Tom")) //Person(Tom,18)
    
    
      /*
        样例类:
        构造器中每个参数都默认为 val;
        自动地生成 equals, hashCode, toString, copy 等方法;
        伴生对象中自动生成 apply 方法,使得可以不用 new 关键字就能构造出相应的对象;
        伴生对象中自动生成 unapply 方法,以支持模式匹配。
    
       */
    }
    
    
    
    伴生对象

    scala中的伴生对象相当于java中的静态成分,加载也快于类。

    类中也可以直接获取到伴生对象中的成员。

    //伴生对象,和类同名,同文件
    object App02{
    
      { //相当于静态代码块,比类中的执行更早
        println("进入"+toString)
      }
    
       //相当于静态变量
      val appObj :String =" --AppObj--"
    
      def main(args: Array[String]): Unit = {
          val app02 = new App02("App02","伴生对象和伴生类")
          println(app02) //App02 , 伴生对象和伴生类 --AppObj--
    
          app02.echo("同名且在同文件中")//App02类方法随意输出点什么吧 :同名且在同文件中
    
          println(app02.desc)  //伴生对象和伴生类
    
    		/*
    				进入obj.App02$@52a86356
                    开始初始化。。。App02 , null --AppObj--
                    App02 , 伴生对象和伴生类 --AppObj--
                    App02类方法随意输出点什么吧 :同名且在同文件中
                    伴生对象和伴生类
    		
    		*/
    
      }
    }
    
    //类 ,类名后跟着主构造器,默认是val类型
    class App02(className :String) {
    
      var desc :String = _ // 使用 _占位
    
      {
        println("开始初始化。。。"+toString)
      }
    
    
      //辅助构造器
      def this(className:String,desc:String)={
        this(className) //需要调用主构造或者其他辅构造
        this.desc =desc
      }
    
    
      override def toString: String = {//重写toString,这里能拿到伴生对象的属性
        this.className + " , " +this.desc +App02.appObj
      }
    
    
    
      def echo(msg:String)={
        println("App02类方法随意输出点什么吧 :"+msg)
      }
    
    
    }
    
    
    
    
    
    可见性、getter/setter

    类默认是public的,protected private 需要指定。

    变量可见性:getter 和 setter 属性与声明变量时使用的关键字有关,当然也可以使用protected private 指定:

    • 使用 var 关键字:变量同时拥有 getter 和 setter 属性;
    • 使用 val 关键字:变量只拥有 getter 属性;
    • 使用 private[this]:变量既没有 getter 属性、也没有 setter 属性,只能通过内部的方法访问;
    • @BeanProperty注解让具备可调用的gettersetter方法
    object App03{
    
    
      def main(args: Array[String]): Unit = {
    
        val app03 = new App03("App03")
    
        app03.setDesc("使用@BeanProperty注解,添加getter/setter")
    
        println(app03.getDesc)//注解带来的方法
    
    	println(app03.desc) //var带来的getter属性
      }
    }
    
    //private[this]
    class App03(className :String) {
    
      //默认是没有getter/setter的
      @BeanProperty var desc :String = _ // 使用 _占位
    
      {
        println(className)
      }
    
    }
    
    
    继承、抽象类

    使用extends关键字来继承类、抽象类。

    除去private修饰的字段方法,其他都可以被子类继承。

    Scala支持各种类型的继承,包括单一,多层次,多重和混合。可以在类中使用单一,多层次和层次结构。多重和混合只能通过使用trait来实现。

    object App04{
    
    
      def main(args: Array[String]): Unit = {
        val app04 = new App04
    	//除了被private关键字修饰的部分,其他都可以被继承
        println(app04.name)
        println(app04.ptName)
        println(app04.baseM())
        println(app04.baseF())
    
    
        println(app04.geDetail)
        println(app04.oo)
    
    
      }
    }
    
    //继承类
    class App04  extends  BaseApp  {}
    
    //继承抽象类
    class BaseApp  extends AbstractApp {
    
      val name ="BaseApp"
      protected val ptName ="pt" 
    
      //来自抽象类
      override val oo: String = "okok"
      //来自抽象类
      override def geDetail: String = {
        "Abstract"
      }
    
      def baseM()={
        "BaseMethod"
      }
    
      def baseF = ()=>{
        "BaseFunc"
      }
    
    
    }
    
    //抽象类
    abstract class AbstractApp{
      //1、定义字段,无需赋值占位
      val oo:String
    
      // 2.定义抽象方法
      def geDetail: String
    
      // 3. scala 的抽象类允许定义具体方法
      def print(): Unit = {
        println("抽象类中的默认方法")
      }
    }
    
    
    

    在 Scala 的类中,每个辅助构造器都必须首先调用其他构造器或主构造器,这样就导致了子类的辅助构造器永远无法直接调用超类的构造器,只有主构造器才能调用超类的构造器。

    所以想要调用超类的构造器,代码示例如下:

    class App04(className:String)  extends  BaseApp(className:String) {
      
    }
    
    
    trait(特质)

    trait 等价于 Java 8 中的接口,因为 trait 中既能定义抽象方法/字段,也能定义具体方法/字段。

    extends 、with关键字用于混入trait,添加多个特质.

    //混入多个trait
    class App05  extends BaseTrait1 with BaseTrait2{
        
        //抽象的都要重写
      override var tName: String = "trait mixin"
    
      override def tM(msg: String): Unit = {
        println("Msg: "+msg)
      }
    }
    
    
    
    // 1.特质使用 trait 关键字修饰
    trait  BaseTrait1  {
      // 抽象字段
      var tName:String
      // 具体字段
      var tType = "FILE"
    
    }
    
    trait  BaseTrait2  {
    
      // 抽象方法
      def tM(msg: String)
    
      // 具体方法
      def tInfo(msg: String): Unit = {
        println("INFO:" + msg)
      }
    }
    
    
    
    

    trait 有默认的无参构造器,但是不支持有参构造器, trait 的调用是由右到左开始生效的

  • 相关阅读:
    jmeter工具应用1
    django1
    5.自动化测试模型
    4.清除cookie操作
    2.操作浏览器
    3.8种元素定位
    1.介绍与环境安装
    模块
    urllib库
    自动化测试PO模式
  • 原文地址:https://www.cnblogs.com/cgl-dong/p/14200186.html
Copyright © 2011-2022 走看看