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
的文件,写入代码,然后使用scalac
、scala
编译运行即可,和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
方法。
Java
在scala
中可以使用,但有时需要做一定的转换。
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 位有符号值,范围从-128 至127 |
2 | Short | 16 位有符号值,范围从-32768 至32767 |
3 | Int | 32 位有符号值,范围从-2147483648 至2147483647 |
4 | Long | 64 位有符号值,范围从-9223372036854775808 至9223372036854775807 |
5 | Float | 32 位IEEE 754单精度浮点值 |
6 | Double | 64 位IEEE 754双精度浮点值 |
7 | Char | 16 位无符号Unicode 字符。范围从U+0000 到U+FFFF |
8 | String | 一个Char 类型序列 |
9 | Boolean | 文字值true 或文字值false |
10 | Unit | 对应于无值 |
11 | Null | null 或空引用 |
12 | Nothing | 每种其他类型的亚型; 不包括无值 |
13 | Any | 任何类型的超类型; 任何对象的类型为Any |
14 | AnyRef | 任何引用类型的超类型 |
上面列出的所有数据类型都是对象。Scala中没有类似Java中那样的原始类型。 这意味着您可以调用Int
,Long
等方法。
连运算符都是方法,+
==.+()
。(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 else
和java
唯一的区别在于是有返回值的。
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 //普通数组转缓冲
scala
和java
的集合是可以互相转换的(不同版本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
mutable
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 的调用是由右到左开始生效的