scala学习笔记

##第一课 基础语法
1,val声明量,var声明变量
2,·基本数据类型:Byte、Char、Short、Int、Long、Float、Double、Boolean,没有java的封装类
3,基本操作符:scala的算术操作符与java的算术操作符也没有什么区别,比如+、-、*、/、%等,以及&、|、^、>>、<<等。没有++,–等操作
4,如果调用函数时,不需要传递参数,则scala允许调用函数时省略括号的,例如,“Hello World”.distinct
5,apply函数:Scala中的apply函数是非常特殊的一种函数,在Scala的object中,可以声明apply函数。而使用“类名()”的形式,其实就是“类名.apply()”的一种缩写。通常使用这种方式来构造类的对象,而不是使用“new 类名()”的方式。
例如,“Hello World”(6),因为在StringOps类中有def apply(n: Int): Char的函数定义,所以"Hello World"(6),实际上是"Hello World".apply(1)的缩写,输出e
例如,Array(1, 2, 3, 4),实际上是用Array object的apply()函数来创建Array类的实例,也就是一个数组

##第二课 条件控制语句
1,if表达式的定义:在Scala中,if表达式是有值的,就是if或者else中最后一行语句返回的值。·例如,var age = 30; age = if (age > 18) 1 else 0 ;
2,当if和else语句返回值类型不同时,取公共的父类型作为返回值类型
例如,if(age > 18) “adult” else 0,此时if和else的值分别是String和Int,则表达式的值是Any,Any是String和Int的公共父类型
如果if后面没有跟else,则默认else的值是Unit,也用()表示,类似于java中的void或者null。例如,val age = 12; if(age>18) “adult”。此时就相当于if(age > 18) “adult” else ()。
3,将if语句放在多行中:默认情况下,REPL(命令行窗口)只能解释一行语句,但是if表达式通常需要放在多行,可以使用{}的方式,比如以下方式,或者使用:paste和ctrl+D的方式。例如:if(age > 18) { “adult” } else if(age > 12) “teenager” else “children”
4,块表达式:块表达式,指的就是{}中的值,其中可以包含多条语句,最后一个语句的值就是块表达式的返回值。
5,默认情况下,scala不需要语句终结符,默认将每一行作为一个语句
6,·print和println:print打印时不会加换行符,而println打印时会加一个换行符。printf:printf可以用于进行格式化。·例如,printf(“Hi, my name is %s, I’m %d years old.\n”, “Leo”, 30)
7,readLine: readLine允许我们从控制台读取用户输入的数据,类似于java中的System.in和Scanner的作用。
8,·while do循环:Scala有while do循环,基本语义与Java相同
9,Scala没有for循环,只能使用while替代for循环,或者使用简易版的for语句
简易版for语句:var n = 10; for(i <- 1 to n) println(i) 结果1-10
·或者使用until,表式不达到上限:for(i <- 1 until n) println(i) 结果1-9
·也可以对字符串进行遍历,类似于java的增强for循环,for(c <- “Hello World”) print©
10,scala没有提供类似于java的break语句,但是可以使用boolean类型变量、return或者Breaks的break函数来替代使用。

import scala.util.control.Breaks._
breakable {
	 var n = 10
	 for(c <- "Hello World") {
			 if(n == 5) break;
			 print(c)
			 n -= 1
		 }
}

11,多重for循环

for(i <- 1 to 9; j <- 1 to 9) {
  if(j == 9) {
    println(i * j)
  } else {
    print(i * j + " ")
  }
}

12,·if守卫:取偶数 for(i <- 1 to 100 if i % 2 == 0) println(i)
13,·for推导式:构造集合 for(i <- 1 to 10) yield i
##第三课 函数入门
1,需要定义函数的函数名、参数、函数体
def sayHello(name: String, age: Int) :int = {age }
函数调用:var age = sayHello(“hmwang”,30)
Scala要求必须给出所有参数的类型,但是不一定给出函数返回值的类型,sacala可以自行推断返回值的类型,但递归函数必须给定返回值类型

#斐波那契数列
def fab(n: Int): Int = {
  if(n <= 1) 1
  else fab(n - 1) + fab(n - 2)
}

2,默认参数
例,
def sayHello(firstName: String, middleName: String = "William", lastName: String = "Croft") = firstName + " " + middleName + " " + lastName
3,带名参数:在调用函数时,也可以不按照函数定义的参数顺序来传递参数,而是使用带名参数的方式来传递。
例:sayHello(firstName = “Mick”, lastName = “Nina”, middleName = “Jack”)
还可以混合使用未命名参数和带名参数,但是未命名参数必须排在带名参数前面。
例sayHello(“Mick”, lastName = “Nina”, middleName = “Jack”)
4,可变参数:在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数。

def sum(nums: Int*) = {
  var res = 0
  for (num <- nums) res += num
  res
}

sum(1, 2, 3, 4, 5)

在如果想要将一个已有的序列直接调用变长参数函数,是不对的。比如val s = sum(1 to 5,此时需要使用Scala特殊的语法将参数定义为序列,让Scala解释器能够识别。这种语法非常有用!一定要好好主意,在spark的源码中大量地使用到了。
val s = sum(1 to 5: _*)
实例:

def sum2(nums: Int*): Int = {
  if (nums.length == 0) 0
  else nums.head + sum2(nums.tail: _*)
}

6,过程:在Scala中,定义函数时,如果函数体直接包裹在了花括号里面,而没有使用=连接,则函数的返回值类型就是Unit。这样的函数就被称之为过程。过程通常用于不需要返回值的函数。例:def sayHello(name: String) { print("Hello, " + name); "Hello, " + name } 或者def sayHello(name: String): Unit = "Hello, " + name
7,lazy:如果将一个变量声明为lazy,则只有在第一次使用该变量时,变量对应的表达式才会发生计算
例:
import scala.io.Source._ lazy val lines = fromFile("C://Users//Administrator//Desktop//test.txt").mkString
8,异常:

try {
  throw new IllegalArgumentException("x should not be negative")
} catch {
  case _: IllegalArgumentException => println("Illegal Argument!")
} finally {
  print("release resources!")
}

try {
  throw new IOException("user defined exception")
} catch {
  case e1: IllegalArgumentException => println("illegal argument")
  case e2: IOException => println("io exception")
}

##第四课 数组
1,Array:在Scala中,Array代表的含义与Java中类似,也是长度不可改变的数组。此外,由于Scala与Java都是运行在JVM中,双方可以互相调用,因此Scala数组的底层实际上是Java数组。例如字符串数组在底层就是Java的String[],整数数组在底层就是Java的Int[]。

var array = new Array(Int)[10]//长度不变
var array = Array("hello","world");

2,ArrayBuffer:在Scala中,如果需要类似于Java中的ArrayList这种长度可变的集合类,则可以使用ArrayBuffer。

// 如果不想每次都使用全限定名,则可以预先导入ArrayBuffer类
import scala.collection.mutable.ArrayBuffer
// 使用ArrayBuffer()的方式可以创建一个空的ArrayBuffer
val b = ArrayBuffer[Int]()
// 使用+=操作符,可以添加一个元素,或者多个元素
// 这个语法必须要谨记在心!因为spark源码里大量使用了这种集合操作语法!
b += 1
b += (2, 3, 4, 5)
// 使用++=操作符,可以添加其他集合中的所有元素
b ++= Array(6, 7, 8, 9, 10)
// 使用trimEnd()函数,可以从尾部截断指定个数的元素
b.trimEnd(5)
// 使用insert()函数可以在指定位置插入元素
// 但是这种操作效率很低,因为需要移动指定位置后的所有元素
b.insert(5, 6)
b.insert(6, 7, 8, 9, 10)
// 使用remove()函数可以移除指定位置的元素
b.remove(1)
b.remove(1, 3)
// Array与ArrayBuffer可以互相进行转换
b.toArray
a.toBuffer

3,循环和遍历Array和ArrayBuffer

// 使用for循环和until遍历Array / ArrayBuffer
// 使until是RichInt提供的函数
for (i <- 0 until b.length)
  println(b(i))
// 跳跃遍历Array / ArrayBuffer
for(i <- 0 until (b.length, 2))
  println(b(i))
// 从尾部遍历Array / ArrayBuffer
for(i <- (0 until b.length).reverse)
  println(b(i))
// 使用“增强for循环”遍历Array / ArrayBuffer
for (e <- b)
  println(e)

4,数组常见操作

// 数组元素求和
val a = Array(1, 2, 3, 4, 5)
val sum = a.sum
// 获取数组最大值
val max = a.max
// 对数组进行排序
scala.util.Sorting.quickSort(a)
// 获取数组中所有元素内容
a.mkString
a.mkString(", ")
a.mkString("<", ",", ">")
// toString函数
a.toString
b.toString

5,使用yield和函数式编程转换数组

// 对Array进行转换,获取的还是Array
val a = Array(1, 2, 3, 4, 5)
val a2 = for (ele <- a) yield ele * ele
// 对ArrayBuffer进行转换,获取的还是ArrayBuffer
val b = ArrayBuffer[Int]()
b += (1, 2, 3, 4, 5)
val b2 = for (ele <- b) yield ele * ele
// 结合if守卫,仅转换需要的元素
val a3 = for (ele <- if ele % 2 == 0) yield ele * ele

// 使用函数式编程转换数组(通常使用第一种方式)
a.filter(_ % 2 == 0).map(2 * _)
a.filter { _ % 2 == 0 } map { 2 * _ }
算法案例:移除第一个负数之后的所有负数
// 构建数组
val a = ArrayBuffer[Int]()
a += (1, 2, 3, 4, 5, -1, -3, -5, -9)
// 每发现一个第一个负数之后的负数,就进行移除,性能较差,多次移动数组
var foundFirstNegative = false
var arrayLength = a.length
var index = 0
while (index < arrayLength) {
  if (a(index) >= 0) {
    index += 1
  } else {
    if (!foundFirstNegative) { foundFirstNegative = true; index += 1 }
    else { a.remove(index); arrayLength -= 1 }
  }
} 
##改良版
// 重新构建数组
val a = ArrayBuffer[Int]()
a += (1, 2, 3, 4, 5, -1, -3, -5, -9)
// 每记录所有不需要移除的元素的索引,稍后一次性移除所有需要移除的元素
// 性能较高,数组内的元素迁移只要执行一次即可
var foundFirstNegative = false
val keepIndexes = for (i <- 0 until a.length if !foundFirstNegative || a(i) >= 0) yield {
  if (a(i) < 0) foundFirstNegative = true
  i
}
for (i <- 0 until keepIndexes.length) { a(i) = a(keepIndexes(i)) }
a.trimEnd(a.length - keepIndexes.length)

6,Map

// 创建一个不可变的Map
val ages = Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23)
ages("Leo") = 31
// 创建一个可变的Map
val ages = scala.collection.mutable.Map("Leo" -> 30, "Jen" -> 25, "Jack" -> 23)
ages("Leo") = 31
// 使用另外一种方式定义Map元素
val ages = Map(("Leo", 30), ("Jen", 25), ("Jack", 23))
// 创建一个空的HashMap
val ages = new scala.collection.mutable.HashMap[String, Int]

// 获取指定key对应的value,如果key不存在,会报错
val leoAge = ages("Leo")
val leoAge = ages("leo")

// 使用contains函数检查key是否存在
val leoAge = if (ages.contains("leo")) ages("leo") else 0

// getOrElse函数
val leoAge = ages.getOrElse("leo", 0)

// 更新Map的元素
ages("Leo") = 31
// 增加多个元素
ages += ("Mike" -> 35, "Tom" -> 40)
// 移除元素
ages -= "Mike"
// 更新不可变的map
val ages2 = ages + ("Mike" -> 36, "Tom" -> 40)
// 移除不可变map的元素
val ages3 = ages - "Tom"

遍历Map

// 遍历map的entrySet
for ((key, value) <- ages) println(key + " " + value)
// 遍历map的key
for (key <- ages.keySet) println(key)
// 遍历map的value
for (value <- ages.values) println(value)
// 生成新map,反转key和value
for ((key, value) <- ages) yield (value, key)

SortedMap和LinkedHashMap

// SortedMap可以自动对Map的key的排序
val ages = scala.collection.immutable.SortedMap("leo" -> 30, "alice" -> 15, "jen" -> 25)

// LinkedHashMap可以记住插入entry的顺序
val ages = new scala.collection.mutable.LinkedHashMap[String, Int]
ages("leo") = 30
ages("alice") = 15
ages("jen") = 25

Tuple

// 简单Tuple
val t = ("leo", 30)

// 访问Tuple
t._1

// zip操作
val names = Array("leo", "jack", "mike")
val ages = Array(30, 24, 26)
val nameAges = names.zip(ages)
for ((name, age) <- nameAges) println(name + ": " + age)

##第五课 面向对象编程
1,类

// 定义类,包含field以及方法
class HelloWorld {
  private var name = "leo"
  def sayHello() { print("Hello, " + name) }  
  def getName = name
}

// 创建类的对象,并调用其方法
val helloWorld = new HelloWorld
helloWorld.sayHello() 
print(helloWorld.getName) // 也可以不加括号,如果定义方法时不带括号,则调用方法时也不能带括号

//getter与setter
// 定义不带private的var field,此时scala生成的面向JVM的类时,会定义为private的name字段,并提供public的getter和setter方法
// 而如果使用private修饰field,则生成的getter和setter也是private的
// 如果定义val field,则只会生成getter方法
// 如果不希望生成setter和getter方法,则将field声明为private[this]
class Student {
  var name = "leo"
}

// 调用getter和setter方法,分别叫做name和name_ =
val leo = new Student
print(leo.name)
leo.name = "leo1"
//自定义getter和setter
// 如果只是希望拥有简单的getter和setter方法,那么就按照scala提供的语法规则,根据需求为field选择合适的修饰符就好:var、val、private、private[this]
// 但是如果希望能够自己对getter与setter进行控制,则可以自定义getter与setter方法
// 自定义setter方法的时候一定要注意scala的语法限制,签名、=、参数间不能有空格
class Student {
  private var myName = "leo"
  def name = "your name is " + myName
  def name_=(newValue: String)  {
    print("you cannot edit your name!!!")
  }
}

val leo = new Student
print(leo.name)
leo.name = "leo1"
//仅暴露field的getter方法
// 如果你不希望field有setter方法,则可以定义为val,但是此时就再也不能更改field的值了
// 但是如果希望能够仅仅暴露出一个getter方法,并且还能通过某些方法更改field的值,那么需要综合使用private以及自定义getter方法
// 此时,由于field是private的,所以setter和getter都是private,对外界没有暴露;自己可以实现修改field值的方法;自己可以覆盖getter方法
class Student {
  private var myName = "leo"

  def updateName(newName: String) { 
    if(newName == "leo1") myName = newName 
    else print("not accept this new name!!!")
  }

  def name = "your name is " + myName
}

//private[this] 的使用
// 如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以直接访问类的其他对象的private field
// 这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对象私有的field,只有本对象内可以访问到
class Student {
  private var myAge = 0
  def age_=(newValue: Int) { 
    if (newValue > 0) myAge = newValue 
    else print("illegal age!") 
  }
  def age = myAge
  def older(s: Student) = {
    myAge > s.myAge
  }
}
//生成Java风格的getter和setter
// Scala的getter和setter方法的命名与java是不同的,是field和field_=的方式
// 如果要让scala自动生成java风格的getter和setter方法,只要给field添加@BeanProperty注解即可
// 此时会生成4个方法,name: String、name_=(newValue: String): Unit、getName(): String、setName(newValue: String): Unit
import scala.reflect.BeanProperty
class Student {
  @BeanProperty var name: String = _ //跟Java不同,必须进行初始化
}
class Student(@BeanProperty var name: String)

val s = new Student
s.setName("leo")
s.getName()

2,辅助构造函数

// Scala中,可以给类定义多个辅助constructor,类似于java中的构造函数重载
// 辅助constructor之间可以互相调用,而且必须第一行调用主constructor
class Student {
  private var name = ""
  private var age = 0
  def this(name: String) {
    this()
    this.name = name
  }
  def this(name: String, age: Int) {
    this(name)
    this.age = age
  }
}

3,主构造函数

// Scala中,主constructor是与类名放在一起的,与java不同
// 而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码,这点感觉没有java那么清晰
class Student(val name: String, val age: Int) {
  println("your name is " + name + ", your age is " + age)
}

// 主constructor中还可以通过使用默认参数,来给参数默认的值
class Student(val name: String = "leo", val age: Int = 30) {
  println("your name is " + name + ", your age is " + age)
}

// 如果主constrcutor传入的参数什么修饰都没有,比如name: String,那么如果类内部的方法使用到了,则会声明为private[this] name;否则没有该field,就只能被constructor代码使用而已

4,内部类

// Scala中,同样可以在类中定义内部类;但是与java不同的是,每个外部类的对象的内部类,都是不同的类
import scala.collection.mutable.ArrayBuffer
class Class {
  class Student(val name: String) {}
  val students = new ArrayBuffer[Student]
  def getStudent(name: String) =  {
    new Student(name)
  }
}
val c1 = new Class
val s1 = c1.getStudent("leo")
c1.students += s1

val c2 = new Class
val s2 = c2.getStudent("leo")
c1.students += s2

5,object
// object,相当于class的单个实例,通常在里面放一些静态的field或者method
// 第一次调用object的方法时,就会执行object的constructor,也就是object内部不在method中的代码;但是object不能定义接受参数的constructor
// 注意,object的constructor只会在其第一次被调用时执行一次,以后再次调用就不会再次执行constructor了
// object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法

object Person {
  private var eyeNum = 2
  println("this Person object!")
  def getEyeNum = eyeNum
}

object也可以继承抽象类,object的功能其实和class类似,除了不能定义接受参数的constructor之外, object也可以继承抽象类,并覆盖抽象类中的方法

abstract class Hello(var message: String) {
  def sayHello(name: String): Unit
}

object HelloImpl extends Hello("hello") {
  override def sayHello(name: String) = {
    println(message + ", " + name)
  }
}

// object中非常重要的一个特殊方法,就是apply方法
// 通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能
// 而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式地调用伴生对象得apply方法,这样会让对象创建更加简洁

// object中非常重要的一个特殊方法,就是apply方法
// 通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能
// 而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式地调用伴生对象得apply方法,这样会让对象创建更加简洁

// 比如,Array类的伴生对象的apply方法就实现了接收可变数量的参数,并创建一个Array对象的功能
val a = Array(1, 2, 3, 4, 5)

// 比如,定义自己的伴生类和伴生对象
class Person(val name: String)
object Person {
  def apply(name: String) = new Person(name)
}

6,伴生对象
// 如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类
// 伴生类和伴生对象必须存放在一个.scala文件之中
// 伴生类和伴生对象,最大的特点就在于,互相可以访问private field

object Person {
  private val eyeNum = 2
  def getEyeNum = eyeNum
}
class Person(val name: String, val age: Int) {
  def sayHello = println("Hi, " + name + ", I guess you are " + age + " years old!" + ", and usually you must have " + Person.eyeNum + " eyes.")
}

7,main方法
// 就如同java中,如果要运行一个程序,必须编写一个包含main方法类一样;在scala中,如果要运行一个应用程序,那么必须有一个main方法,作为入口

// scala中的main方法定义为def main(args: Array[String]),而且必须定义在object中
object HelloWorld {
  def main(args: Array[String]) {
    println("Hello World!!!")
  }
}
// 除了自己实现main方法之外,还可以继承App Trait,然后将需要在main方法中运行的代码,直接作为object的constructor代码;而且用args可以接受传入的参数
object HelloWorld extends App {
  if (args.length > 0) println("hello, " + args(0))
  else println("Hello World!!!")
}
// App Trait的工作原理为:App Trait继承自DelayedInit Trait,scalac命令进行编译时,会把继承App Trait的object的constructor代码都放到DelayedInit Trait的delayedInit方法中执行

// 如果要运行上述代码,需要将其放入.scala文件,然后先使用scalac编译,再用scala执行
scalac HelloWorld.scala
scala -Dscala.time HelloWorld

8,用object构造枚举类型
// Scala没有直接提供类似于Java中的Enum这样的枚举特性,如果要实现枚举,则需要用object继承Enumeration类,并且调用Value方法来初始化枚举值

object Season extends Enumeration {
  val SPRING, SUMMER, AUTUMN, WINTER = Value
}
// 还可以通过Value传入枚举值的id和name,通过id和toString可以获取; 还可以通过id和name来查找枚举值
object Season extends Enumeration {
  val SPRING = Value(0, "spring")
  val SUMMER = Value(1, "summer")
  val AUTUMN = Value(2, "autumn")
  val WINTER = Value(3, "winter")
}
Season(0)
Season.withName("spring")
// 使用枚举object.values可以遍历枚举值
for (ele <- Season.values) println(ele)

##第六课,继承
1,// Scala中,让子类继承父类,与Java一样,也是使用extends关键字
// 继承就代表,子类可以从父类继承父类的field和method;然后子类可以在自己内部放入父类所没有,子类特有的field和method;使用继承可以有效复用代码
// 子类可以覆盖父类的field和method;但是如果父类用final修饰,field和method用final修饰,则该类是无法被继承的,field和method是无法被覆盖的

class Person {
  private var name = "leo"
  def getName = name
}
class Student extends Person {
  private var score = "A"
  def getScore = score
}

2,override和supper

// Scala中,如果子类要覆盖一个父类中的非抽象方法,则必须使用override关键字
// override关键字可以帮助我们尽早地发现代码里的错误,比如:override修饰的父类方法的方法名我们拼写错了;比如要覆盖的父类方法的参数我们写错了;等等
// 此外,在子类覆盖父类方法之后,如果我们在子类中就是要调用父类的被覆盖的方法呢?那就可以使用super关键字,显式地指定要调用父类的方法

class Person {
  private var name = "leo"
  def getName = name
}
class Student extends Person {
  private var score = "A"
  def getScore = score
  override def getName = "Hi, I'm " + super.getName
}

3, isInstanceOf和asInstanceOf

// 如果我们创建了子类的对象,但是又将其赋予了父类类型的变量。则在后续的程序中,我们又需要将父类类型的变量转换为子类类型的变量,应该如何做?
// 首先,需要使用isInstanceOf判断对象是否是指定类的对象,如果是的话,则可以使用asInstanceOf将对象转换为指定类型
// 注意,如果对象是null,则isInstanceOf一定返回false,asInstanceOf一定返回null
// 注意,如果没有用isInstanceOf先判断对象是否为指定类的实例,就直接用asInstanceOf转换,则可能会抛出异常

class Person
class Student extends Person
val p: Person =  new Student
var s: Student = null
if (p.isInstanceOf[Student]) s = p.asInstanceOf[Student]

4,getClass和classOf
// isInstanceOf只能判断出对象是否是指定类以及其子类的对象,而不能精确判断出,对象就是指定类的对象
// 如果要求精确地判断对象就是指定类的对象,那么就只能使用getClass和classOf了
// 对象.getClass可以精确获取对象的类,classOf[类]可以精确获取类,然后使用==操作符即可判断

`class Person
class Student extends Person
val p: Person = new Student
p.isInstanceOf[Person]
p.getClass == classOf[Person]
p.getClass == classOf[Student]

5,使用模式匹配
// 但是在实际开发中,比如spark的源码中,大量的地方都是使用了模式匹配的方式来进行类型的判断,这种方式更加地简洁明了,而且代码得可维护性和可扩展性也非常的高
// 使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断主要是该类以及该类的子类的对象即可,不是精准判断的

class Person
class Student extends Person
val p: Person = new Student

p match {
  case per: Person => println("it's Person's object")
  case _  => println("unknown type")
}

5,protected
/ 跟java一样,scala中同样可以使用protected关键字来修饰field和method,这样在子类中就不需要super关键字,直接就可以访问field和method
// 还可以使用protected[this],则只能在当前子类对象中访问父类的field和method,无法通过其他子类对象访问父类的field和method

class Person {
  protected var name: String = "leo"
  protected[this] var hobby: String = "game"
} 
class Student extends Person {
  def sayHello = println("Hello, " + name)
  def makeFriends(s: Student) {
    println("my hobby is " + hobby + ", your hobby is " + s.hobby)
  }
}

6,抽象类
// 如果在父类中,有某些方法无法立即实现,而需要依赖不同的子来来覆盖,重写实现自己不同的方法实现。此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法。
// 而一个类中如果有一个抽象方法,那么类就必须用abstract来声明为抽象类,此时抽象类是不可以实例化的
// 在子类中覆盖抽象类的抽象方法时,不需要使用override关键字

abstract class Person(val name: String) {
  def sayHello: Unit
}
class Student(name: String) extends Person(name) {
  def sayHello: Unit = println("Hello, " + name)
}

7,抽象filed
// 如果在父类中,定义了field,但是没有给出初始值,则此field为抽象field
// 抽象field意味着,scala会根据自己的规则,为var或val类型的field生成对应的getter和setter方法,但是父类中是没有该field的
// 子类必须覆盖field,以定义自己的具体field,并且覆盖抽象field,不需要使用override关键字

abstract class Person {
  val name: String
}
class Student extends Person {
  val name: String = "leo"
}

你可能感兴趣的:(scala,大数据)