Scala面向对象编程

类的定义
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关键字
/子类中接收时,就不要用任何valvar来修饰了,否则会认为是子类要覆盖父类的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]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值