Scala 面向对象(一)apply+main+extends+super+isInstanceOf+asInstanceOf+classOf关键字

1.   类、对象、继承、特质

Scala的类与Java、C++的类比起来更简洁,学完之后你会更爱Scala!!!

1.1.   类

1.1.1.    类的定义

package cn.gec.class_demo


/**
*
Scala中,类并不用声明为public类型的。
* Scala源文件中可以包含多个类,所有这些类都具有共有可见性。
*/
class Person {

  //用val修饰的变量是可读属性,有getter但没有setter(相当与Java中用final修饰的变量)
  val id="9527"

  //用var修饰的变量都既有getter,又有setter
  var age:Int=18

  //类私有字段,只能在类的内部使用或者伴生对象中访问
  private var name : String = "唐伯虎"

  //类私有字段,访问权限更加严格的,该字段在当前类中被访问
  //在伴生对象里面也不可以访问
  private[this] var pet = "小强"

}

//伴生对象(这个名字和类名相同,叫伴生对象)
object Person{
  def main(args: Array[String]): Unit = {
    val p=new Person

    //如果是下面的修改,发现下面有红线,说明val类型的不支持重新赋值,但是可以获取到值
    //p.id = "123"
    println(p.id)
    //打印age
    println(p.age)
    //打印name,伴生对象中可以在访问private变量
    println(p.name)
    //由于pet字段用private[this]修饰,伴生对象中访问不到pet变量
    //p.pet(访问不到)

  }
}
 

 

1.1.2.    构造器

Scala中的每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起。

注意:主构造器会执行类定义中的所有语句。

package cn.gec.class_demo
/**
  *
每个类都有主构造器,主构造器的参数直接放置类名后面,与类交织在一起
 
*/
class Student(val name:String,var age:Int) {

  //主构造器会执行类定义的所有语句
  println("执行主构造器")
  private  var gender="male"
  def this(name:String,age:Int,gender:String){
    //每个辅助构造器执行必须以主构造器或者其他辅助构造器的调用开始
    this(name,age)
    println("执行辅助构造器")
    this.gender=gender
  }
}

object Student {
  def main(args: Array[String]): Unit = {
    val s1=new Student("zhangsan",20)
   
    val s2=new Student("zhangsan",20,"female")
  }
}
 

1.2.   Scala面向对象编程之对象

1.2.1.     Scala中的object

  • object 相当于 class 的单个实例,通常在里面放一些静态的 field 或者 method;

在Scala中没有静态方法和静态字段,但是可以使用object这个语法结构来达到同样的目的。

object作用:

1.存放工具方法和常量

2.高效共享单个不可变的实例

3.单例模式

  • 举例说明:
  • 如果有一个class文件,还有一个与class同名的object文件,那么就称这个object是class的伴生对象,class是object的伴生类;
  • 伴生类和伴生对象必须存放在一个.scala文件中;
  • 伴生类和伴生对象的最大特点是,可以相互访问;
  • 举例说明:


package cn.gec.object_demo
import scala.collection.mutable.ArrayBuffer

class Session{}
object SessionFactory{
  //该部分相当于java中的静态块
   val session=new Session
  //在object中的方法相当于java中的静态方法
  def getSession(): Session ={
    session
 
}

}
object SingletonDemo {
  def main(args: Array[String]) {
    //单例对象,不需要new,用【单例对象名称.方法】调用对象中的方法
    val session1 = SessionFactory.getSession()
    println(session1)
    //单例对象,不需要new,用【单例对象名称.变量】调用对象中成员变量
val session2=SessionFactory.session
    println(session2)
  }
}




 

1.2.2.     Scala中的伴生对象



package cn.gec.object_demo


//伴生类

class Dog {
  val id = 1
  private var name = "gec"
  def printName(): Unit ={
    //在Dog类中可以访问伴生对象Dog的私有属性
    println(Dog.CONSTANT + name )
  }
}
//伴生对象
object Dog {
  //伴生对象中的私有属性
  private val CONSTANT = "汪汪汪 : "
  def main(args: Array[String]) {
    val p = new Dog
    //访问私有的字段name
    p.name = "123"
    p.printName()
  }
}
//执行结果 汪汪汪 : 123


 

1.2.3.     Scala中的apply方法

  • object 中非常重要的一个特殊方法,就是apply方法;
  • apply方法通常是在伴生对象中实现的,其目的是,通过伴生类的构造函数功能,来实现伴生对象的构造函数功能;
  • 通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,...参数n)时apply方法会被调用;
  • 在创建伴生对象或伴生类的对象时,通常不会使用new class/class() 的方式,而是直接使用 class(),隐式的调用伴生对象的 apply 方法,这样会让对象创建的更加简洁;
  • 举例说明:


package cn.gec.object_demo

/**
 *  Array
类的伴生对象中,就实现了可接收变长参数的 apply 方法,
 
* 并通过创建一个 Array 类的实例化对象,实现了伴生对象的构造函数功能
 
*/
// 指定 T 泛型的数据类型,并使用变长参数 xs 接收传参,返回 Array[T] 数组

// 通过 new 关键字创建 xs.length 长的 Array 数组
// 其实就是调用Array伴生类的 constructor进行 Array对象的初始化
//  def apply[T: ClassTag](xs: T*): Array[T] = {
//    val array = new Array[T](xs.length)
//    var i = 0
//    for (x <- xs.iterator) { array(i) = x; i += 1 }
//    array
//  }

object ApplyDemo {
  def main(args: Array[String]) {
    //调用了Array伴生对象的apply方法
    //def apply(x: Int, xs: Int*): Array[Int]
    //arr1中只有一个元素5
    val arr1 = Array(5)

    //new了一个长度为5的array,数组里面包含5个null
    var arr2 = new Array(5)
    println(arr1.toBuffer)
  }
}


 

1.2.4.     Scala中的main方法

  • 同Java一样,如果要运行一个程序,必须要编写一个包含 main 方法的类;
  • 在 Scala 中,也必须要有一个 main 方法,作为入口;
  • Scala 中的 main 方法定义为 def main(args: Array[String]),而且必须定义在 object 中;
  • 除了自己实现 main 方法之外,还可以继承 App Trait,然后,将需要写在 main 方法中运行的代码,直接作为 object 的 constructor 代码即可,而且还可以使用 args 接收传入的参数;
  • 案例说明:

 



package cn.gec.object_demo


//1.在object中定义main方法

object Main_Demo1 {
  def main(args: Array[String]) {
    if(args.length > 0){
      println("Hello, " + args(0))
    }else{
      println("Hello World!")
    }
  }
}
//2.使用继承App Trait ,将需要写在 main 方法中运行的代码
// 直接作为 object 的 constructor 代码即可,
// 而且还可以使用 args 接收传入的参数。

object Main_Demo2 extends App{
    if(args.length > 0){
      println("Hello, " + args(0))
    }else{
      println("Hello World!")
    }
}


 

1.3.   Scala面向对象编程之继承

1.3.1.     Scala中继承(extends)的概念

  • Scala 中,让子类继承父类,与 Java 一样,也是使用 extends 关键字;
  • 继承就代表,子类可继承父类的 field 和 method ,然后子类还可以在自己的内部实现父类没有的,子类特有的 field 和method,使用继承可以有效复用代码;
  • 子类可以覆盖父类的 field 和 method,但是如果父类用 final 修饰,或者 field 和 method 用 final 修饰,则该类是无法被继承的,或者 field 和 method 是无法被覆盖的。
  • private 修饰的 field 和 method 不可以被子类继承,只能在类的内部使用;
  • field 必须要被定义成 val 的形式才能被继承,并且还要使用 override 关键字。 因为 var 修饰的 field 是可变的,在子类中可直接引用被赋值,不需要被继承;即 val 修饰的才允许被继承,var 修饰的只允许被引用。继承就是改变、覆盖的意思。
  • Java 中的访问控制权限,同样适用于 Scala

 

类内部

本包

子类

外部包

 

 

 

 

 

public

 

 

 

 

 

protected

×

 

 

 

 

 

default

×

×

 

 

 

 

 

private

×

×

×

 

 

 

 

 

 


  • 举例说明:
package cn.gec.extends_demo


class Person1 {

  val name="super"
  def getName=this.name
}

class Student1 extends Person1{
  //继承加上关键字
  override
  val
name="sub"

  //子类可以定义自己的field和method
  val score="A"
  def getScore=this.score
}


 

1.3.2.     Scala中override 和 super 关键字

  • Scala中,如果子类要覆盖父类中的一个非抽象方法,必须要使用 override 关键字;子类可以覆盖父类的 val 修饰的field,只要在子类中使用 override 关键字即可。
  • override 关键字可以帮助开发者尽早的发现代码中的错误,比如, override 修饰的父类方法的方法名拼写错误。
  • 此外,在子类覆盖父类方法后,如果在子类中要调用父类中被覆盖的方法,则必须要使用 super 关键字,显示的指出要调用的父类方法。
  • 举例说明:


package cn.gec.extends_demo


class Person2 {

  private val name = "leo"
  val age=50
  def getName = this.name
}

class Student2 extends Person2{
  private val score = "A"
  //子类可以覆盖父类的 val field,使用override关键字
  override
  val
age=30

  def getScore = this.score
 
//覆盖父类非抽象方法,必须要使用 override 关键字

  //同时调用父类的方法,使用super关键字
  override def getName = "your name is " + super.getName
}


 

 

1.3.3.     Scala中isInstanceOf 和 asInstanceOf

如果实例化了子类的对象,但是将其赋予了父类类型的变量,在后续的过程中,又需要将父类类型的变量转换为子类类型的变量,应该如何做?

Class A extends class B

B b=new A

 

  • 首先,需要使用 isInstanceOf 判断对象是否为指定类的对象,如果是的话,则可以使用 asInstanceOf 将对象转换为指定类型;
  • 注意: p.isInstanceOf[XX] 判断 p 是否为 XX 对象的实例;p.asInstanceOf[XX] 把 p 转换成 XX 对象的实例
  • 注意:如果没有用 isInstanceOf 先判断对象是否为指定类的实例,就直接用 asInstanceOf 转换,则可能会抛出异常;
  • 注意:如果对象是 null,则 isInstanceOf 一定返回 false, asInstanceOf 一定返回 null;
  • Scala与Java类型检查和转换

 

Scala

Java

obj.isInstanceOf[C]

obj instanceof C

obj.asInstanceOf[C]

(C)obj

classOf[C]

C.class

 

  • 举例说明:

 



package cn.gec.extends_demo


class Person3 {}

class Student3 extends Person3
object Student3{
    def main (args: Array[String] ) {
    val p: Person3 = new Student3
    var s: Student3 = null
   
//如果对象是 null,则 isInstanceOf 一定返回 false

    println (s.isInstanceOf[Student3])
    // 判断 p 是否为 Student3 对象的实例
  if (p.isInstanceOf[Student3] ) {
    //把 p 转换成 Student3 对象的实例
      s = p.asInstanceOf[Student3]
  }
  println (s.isInstanceOf[Student3] )
  }
}


 

1.3.4.     Scala中getClass 和 classOf


Class A extends class B

B b=new A    b.getClass ==classOf[A]

B b=new B    b.getClass ==classOf[B]

 

  • isInstanceOf 只能判断出对象是否为指定类以及其子类的对象,而不能精确的判断出,对象就是指定类的对象;
  • 如果要求精确地判断出对象就是指定类的对象,那么就只能使用 getClass 和 classOf 了;
  • p.getClass 可以精确地获取对象的类,classOf[XX] 可以精确的获取类,然后使用 == 操作符即可判断;
  • 举例说明:
package cn.gec.extends_demo


class Person4 {}

class Student4 extends Person4
object Student4{
  def main(args: Array[String]) {
    val p:Person4=new Student4
    //判断p是否为Person4类的实例
    println(p.isInstanceOf[Person4])//true
    //判断p的类型是否为Person4类
    println(p.getClass == classOf[Person4])//false
    //判断p的类型是否为Student4类
    println(p.getClass == classOf[Student4])//true
  }
}


 
原文地址:https://www.cnblogs.com/Transkai/p/10934522.html