类的定义
package com.scala //定义包名
// 定义类的时候自动引入主构造函数,参数和类参数一致
// 没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码
// 类参数带val或var修饰的,自动成为类的实例,可以通过对象.变量名引用
// 没有带var或var修饰的,外部无法引用
class Student(val name: String, age:Int) {
require(name.length < 3,"名字长度小于3") //检测参数
println("your name is " + name) //属于主构造函数的代码
}
/*
编译:scalac com/scala/HelloWorld.scala
*/
// 定义类,包含变量和方法示例
class HelloWorld {
//实例变量,必须赋值,修饰符可以有private、private[this]、protected、protected[this]、private[包名]
var name = "leo"
//辅助构造函数,可以有多个,名字都是this,第一行必须调用主构造函数或者其他辅助构造函数
def this(name: String) {
this()//调用主构造函数
this.name = name
}
def getName = name
//如果想自动生成getName和setName方法可以使用注解
@scala.reflect.BeanProperty var name = "leo"
def sayHello() { print("Hello, " + name) } //实例方法
}
- 权限修饰符作用
//使用private修饰的变量,属于类私有,即只能在类的范围访问
//使用private[this]修饰的变量,属于对象私有,即不能直接访问其他对象的该属性
class Student {
private[this] var myAge = 0
def older(s: Student) = {
myAge > s.myAge //错误:不能在当前对象访问其他对象的私有对象属性
}
}
//protected[this],则只能在当前子类对象中访问父类的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+" "+s.name) //不可以访问s.hobby,而s.name可以
}
}
//同理,private[spark]修饰的变量在包spark以及spark子包(如com.spark.core)内可访问,其他不可以
}
定义静态成员变量和方法
- 上面class关键字包含的都是成员变量和方法,声明静态的在哪里呢
在object关键字里面声明静态的field或者method
object不能定义接受参数的constructor
第一次使用object时,会触发object的constructor的执行,也即不在方法块中的代码,仅执行一次
object可以继承抽象类,并覆盖抽象类中的方法
object Person {
var eyeNum = 2
println("this Person object!")
def getEyeNum = eyeNum
}
//使用方式类似java的类变量
Person.eyeNum = 3
伴生对象
- 如果有一个class,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类
- 伴生类和伴生对象必须存放在一个.scala文件之中
- 伴生类和伴生对象,最大的特点就在于,互相可以访问private field
object Person { //搭配使用,放置类Person通用的属性
private val eyeNum = 2
def getEyeNum = eyeNum
}
class Person(val name: String, val age: Int) {
def sayHello = println(Person.eyeNum)
}
对象的创建
- new 包名.类名(),如果无参数,括号可以省略
包名.类名(),这种方式需要object的apply方法支持
apply方法
object中非常重要的一个特殊方法,就是apply方法
通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能
使用Class()的方式,Scala隐式地调用伴生对象的apply方法
示例
class Person(val name: String) object Person { // 返回对象,REPL显示,apply: (name: String)Person def apply(name: String) = new Person(name) } //Person("Leo")默认会调用伴生对象Person的apply方法 val p = Person("Leo")
入口方式
- main方法
//编译命令:scalac 包名/HelloWorld.scala
//运行命令:scala 包名.HelloWorld
object HelloWorld {
// 只能定义在object中
def main(args: Array[String]) {
println("Hello World")
}
}
- 继承App Trait
object HelloWorld extends App {
/*
将需要在main方法中运行的代码,直接作为object的constructor代码;
而且用args可以接受传入的参数
*/
if (args.length > 0) println("hello, " + args(0))
else println("Hello World!!!")
}
继承
- 在覆盖方法情况下,需要调用父类方法可以使用super
- 只能在子类的主constructor中调用父类的constructor
class Person(val name: String)
//使用extends关键字
/子类中接收时,就不要用任何val 或var来修饰了,否则会认为是子类要覆盖父类的field
class Student(name: String, var score: Double) extends Person(name) { //name没有使用var等修饰
def this(name: String) {//辅助构造函数
this(name, 0) //调用主构造函数
}
def this(age: Int) {
this("leo", 0)
//如果子类要覆盖一个父类中的非抽象方法,必须使用override关键字,可以尽早发现错误
//覆盖java.lang.Object中的toString
override def toString = name
}
抽象类
- 抽象类是不可以实例化的
- 在子类中覆盖抽象类的抽象方法时,不需要使用override关键字
- field如果没有给出具体初始值,则为抽象field,子类覆盖也不需override
abstract class Person {
val name: String //抽象field
def sayHello: Unit //抽象方法,仅有方法签名
}
trait特质
- scala不支持类的多继承,但支持多继承trait,使用with关键字
- 与Java接口类似,可以定义抽象方法,也可以定义具体方法
- trait是没有接收参数的构造函数的
- 类可以使用extends关键字继承trait
- 类继承trait后,必须实现其中的抽象方法,实现时不需要使用override关键字
trait HelloTrait {
def sayHello(name: String) //抽象方法
}
class Person(val name: String) extends HelloTrait with Cloneable with Serializable {
def sayHello(name: String) = println("Hello, " + name)
}
- 类中调用多个trait中都有的方法,首先会从最右边的trait的方法开始执行,然后依次往左执行
trait Handler {
def handle(data: String) {println("handler")}
}
trait DataValidHandler extends Handler {
override def handle(data: String) {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandler extends Handler {
override def handle(data: String) {
println("check signature: " + data)
super.handle(data)
}
}
class Person(val name: String) extends SignatureValidHandler with DataValidHandler {
def sayHello = { println("Hello, " + name); handle(name)
}
}
//测试
new Person("name").sayHello
//Hello, name
//check data: name
//check signature: name
//handler
- 实例对象混入trait,
trait Logged {
def log(msg: String) {} //具体方法
}
trait MyLogger extends Logged {
override def log(msg: String) { println("log: " + msg) }
}
class Person(val name: String) extends Logged {
def sayHello {
println("Hi, I'm " + name);
log("sayHello is invoked!")
}
}
//测试
val p1 = new Person("leo")
p1.sayHello
val p2 = new Person("jack") with MyLogger
p2.sayHello //会调用MyLogger的log方法
//无需继承也可
class TestInject{}
val inject = new TestInject() with MyLogger
inject.log("test")
- trait的构造机制
class Person { println("Person's constructor!") }
trait Logger { println("Logger's constructor!") }
trait MyLogger extends Logger { println("MyLogger's constructor!") }
trait TimeLogger extends Logger { println("TimeLogger's constructor!") }
class Student extends Person with MyLogger with TimeLogger {
println("Student's constructor!")
}
trait也是有构造代码的,也就是trait中的,不包含在任何方法中的代码
父类的构造函数执行;
trait的构造代码执行, 多个trait从左到右依次执行;
构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;
所有trait构造完毕之后,子类的构造函数执行
类型转换
- isInstanceOf判断对象是否是指定类及其子类的对象
class Person
class Student extends Person
//测试
val p: Person = new Student
var s: Student = null
if (p.isInstanceOf[Student]) { //true
s = p.asInstanceOf[Student]
}
- getClass和classOf精确判断对象就是指定类的对象
class Person
class Student extends Person
val p: Person = new Student
println(p.getClass == classOf[Person]) //false
println(p.getClass == classOf[Student]) //true
- spark一般使用case做类型判断,如下
p match {
case per: Person => println("it's Person's object")
case _ => println("unknown type") //如果都没匹配默认会来这
}
内部类
- 与java不同的是,每个外部类的对象的内部类,都是不同的类
class Class {
// 内部类Student
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 //错误:外部类对象c1的内部对象Student与外部类对象c2的内部对象不同
匿名内部类
- 定义一个类的没有名称的子类,并直接创建其对象,然后将对象的引用赋予一个变量。之后甚至可以将该匿名子类的对象传递给其他函数。
class Person(protected val name: String) {
def sayHello = "Hello, I'm " + name
}
val p = new Person("leo") { //Person的匿名子类对象
override def sayHello = "Hi, I'm " + name
}
def greeting(p: Person { def sayHello: String }) {//可以传递给该函数
println(p.sayHello)
}
泛型
- 泛型类
class List[T]() {
def add(element: T) = {} //参数是泛型
}
//使用
val list1 = new List[Int]() //创建的时候确定类型
- 泛型函数
def getCard[T](content: T) = {}
getCard[String]("hello world") //调用的时候确定
- 上边界Bounds
//限定为某个类的子类
class List[T<: String]() {
def add(element: T) = {}
}
- 下边界使用R >: Child,即必须是某个类的父类
- View Bounds
implicit def dog2person(dog: Object): Person = {}
class Party[T <% Person](p1: T, p2: T)
//使用
//dog1会被隐式转换为Person,再判断是否是Person子类
val party = new Party[Person](new Student(""), new Dog(""))
- 协变:实现Professional是Master的子类,那么Card[Professionnal]是不是Card[Master]的子类
class Card[+T] (val name: String)
- 逆变:[-T]