Scala类和对象详解

    一、类定义、创建对象

    Scala和Java一样,用class关键字来创建类,eg:

    class Student{

        var score:Int=0

    }

    利用javap -private Student命令可以看到上述Scala文件对应的字节码文件内容为:

    可以看到,编译后成员变量score前面的限定符为private ,Scala默认生成了score()和score_=()方法,JVM由于不允许出现=,所以用$eq代替了。生成的score()对应Java中的setter方法,score_=()对应Java中的getter方法,生成的字节码文件中setter和getter方法是公有的。因此可以直接用new来创建对象。

    val stu = new Student()  

    t.score_=20    //调用setter方法 

    t.score=20    //直接修改,但其实也是调用setter方法

    t.score    //调用getter方法

    当然也可以定义自己的setter,getter方法,如下

    class Student{

        private var privateScore:Int=0

        def score=privateScore

        def score_=(score:Int){

        this.privateScore=score

        }

    }

    对应二进制文件如下:


    可以看到定义的私有成员其自带的setter和getter方法被private修饰,而我们自己的定义的setter和getter方法可以被访问

    val stu=new Student( )

    stu.score    //访问自定义的getter方法

    stu.score = 30    //直接赋值法,其实调用的是setter方法

    stu.score_ = 30    //setter方法

    通过stu.score=30的这种方法赋值,调用者无需知道是通过字段访问还是方法调用来操作的,这就是著名的统一访问原则

    如果类的成员域是val类型的变量,则只会生成getter方法

class Student {
  val score:Int=0
}

    对应二进制文件如下:


    可以看出:val变量对应的Java中final类型的变量,只生成了getter方法,如果将成员域定义为private[this],则不会生成getter、setter方法 

class Student {
  private[this] var score:Int=0
}
对应二进制文件如下:


    Java中,定义JavaBean时,生成的是setXxx()和getXxx()方法,但scala生成的getter()和setter()方法并不是这样的,如果程序也需要自动生成getter和setter方法,需要引入scala.reflect.BeanProperty,然后采用注解的方式修饰变量

class Student {
  @BeanProperty var score:Int=0
}
对应二进制文件如下:


    下图是setter、getter方法的产生规则

来源:scala for the impatient

    二、类的主构造器

     Student.scala文件

  class Student(var name:String,var age:Int)   //括号里的变量为主构造器的参数

  编译后的二进制文件


    主构造器会执行类中方法外的所有语句

class Student(var name: String, var age: Int) {
  //println将作为主构建器中的一部分,在创建对象时被执行
  println("被主构造器执行的代码 ........")

  //重写toString()方法
  override def toString: String = name + ":" + age
  def ceshi()={
    print("不输出")
  }
}
  二进制文件如下:
 
 
  主构造器还可以使用默认参数
class Student(val name:String="name",val score:Int=60){
  println("默认参数主构造器中的代码")
  val age:Int=10
  override def toString: String = name + ":" + age
}

    二进制文件如下:


    主构造器中的参数还可以加访问修饰符

class Student(val name:String="name",private val score:Int=60){
  println("主构造器中的参数加访问修饰符")
  val age:Int=10
  override def toString: String = name + ":" + age
}
二进制文件如下:可以看出private修饰的score,其getter方法是私有的,外部不能访问

    当主构造器的参数不用var或val修饰的时候,参数会生成类的私有val成员,并且不会产生getter和setter方法

    

class Student(name:String,score:Int){
  println("主构造器中的参数不用varval修饰")
  val age:Int=10
  override def toString: String = name + ":" + age
}
  二进制文件如下:

    主构造器参数不加var和val相当于如下代码:

class Student(private[this] val name:String,private[this] val score:Int){
  println("主构造器中的参数不用varval修饰相当于参数前面加private[this]")
  val age:Int=10
  override def toString: String = name + ":" + age
}

    二进制文件如下:


    下图给出了Scala中主构造器参数生成器类成员和方法时的规则


来源:scala for the impatient

    在某些情况下,可能需要禁用主构造器,代码如下:

    //类名后面紧跟private关键字可以将主构造器设为私有,不允许外部使用

    

class Student private(var name:String,var score:Int){
  println("禁用主构造器中的参数")
  val age:Int=10
}
二进制代码如下:

    从上可以看出,编译后的文件主构造器被private修饰了,外部不能访问    

    三、辅助构造函数

    当禁用了主构造器,就必须使用辅助构造函数来创建对象。辅助构造函数具有以下两个特点:(1)辅助构造器名称为this(2)调用辅助构造函数时,必须先调用主构造函数或其他已经定义好的构造函数。

    1、只有辅助构造函数的Student类,注意:在定义辅助构造函数时,需要注意构造函数的顺序,不要将在构造函数中调用之前未定义的辅助构造函数,否则会发声编译错误

class Student {
  private var name: String = null
  private var age: Int = 20
  private var sex: String = "man"
  //辅助构造器
  def this(name:String){
    this()
    this.name=name
  }
  def this(name:String,age:Int){
    this(name)
    this.age=age
  }
  def this(name:String,age:Int,sex:String){
    this(name,age)
    this.sex=sex
  }
}
    二进制文件如下:
 
 
    2、带主构造函数、辅助构造函数的类
    class Student(name: String,age: Int) {
      private var sex:String="man"
      //辅助构造器
     def this(name:String,age:Int,sex:String){
        this(name,age)
        this.sex=sex
      }
    }
    二进制文件如下:

    之前提到过,有时候可能会禁用掉主构造器函数,此时只能通过辅助构造器函数来创建对象

class Student private(name: String,age: Int) {
  private var sex:String="man"
  //辅助构造器
  def this(name:String,age:Int,sex:String){
    this(name,age)
    this.sex=sex
  }
}

    二进制文件如下:


    4、单例对象

    Scala中,如果我们想像Java中一样直接使用类名来调用方法或成员变量的时候,需要创建单例对象来实现,如下

    

object Student{
  private var sex:String="man"
  def describle()={
    println("我是单例对象中的成员方法")
  }

  def main(args: Array[String]): Unit = {
    Student.describle()
  }
}
object Student编码后将生成两个字节码文件,Student$.class和Student.class

Student$.class编译后的二进制文件如下:
 
 
Student.class编译后的二进制文件如下:


    可以看出,object Student最终生成了两个类,分别是Student和Student$,它们都是final类型的,而且Student的构造方法是私有的,通过静态成员域public static final test.Student$ MODULES$对Student进行引用,这其实Java语言中单例实现方式

    5、伴生类和伴生对象

    在同一个Scala文件中,存在相同名称的object和class,此时object 称为class的伴生对象,而class称为object的伴生类,伴生类和伴生对象本质上是不同的两个类,只不过它们两个之间可以互相访问对方的任何成员变量和方法,包括私有的。

    6、apply方法

    利用apply方法可以直接利用类名创建对象,比如说创建集合,val list=List("A","B","C"),这其实就是调用

val list=List.apply("A","B","C"),只不过第一种方法更简洁,但需要指出的是,这种创建方式仍然免不了new,它后面仍是new的方式,只不过我们在使用的时候可以省去new关键字

    7、应用程序对象

    我们都知道,main方法是程序的入口,但scala可以通过继承App来省去定义main方法,此时代码更加简洁,App其实是一种trait,它帮助定义了main方法

    8、抽象类

    抽象类不能被实例化,其中定义了抽象方法,如果要具体实现需要被非抽象子类继承来实现。除抽象方法外,抽象类中还可以有抽象字段。

    

猜你喜欢

转载自blog.csdn.net/weixin_40083942/article/details/79324585
今日推荐