Scala语法入门

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

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

一、Hello World

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

0、Hello World

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

C:\Users\scala>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\n ",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注解让具备可调用的getter\setter方法
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 的调用是由右到左开始生效的

posted @ 2020-12-28 09:53  cgl_dong  阅读(169)  评论(0编辑  收藏  举报