Scala入门到精通——第六节:类和对象(一)

来源:互联网 发布:python __ 编辑:程序博客网 时间:2024/05/21 23:33

本节主要内容

1 类定义、创建对象
2 主构造器
3 辅助构造器

类定义、创建对象

//采用关键字class定义class Person {  //类成员必须初始化,否则会报错  //这里定义的是一个公有成员  var name:String=null}

Person类在编译后会生成Person.class文件
这里写图片描述
利用javap -prviate Person命令查看字节码文件内容,可以看得到以下内容

D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String name;  public java.lang.String name();  public void name_$eq(java.lang.String);  public cn.scala.xtwy.Person();}

从字节码文件内容可以看到:虽然我们只在Person类中定义了一个类成员(域)name,类型为String,但Scala会默认帮我们生成name()与name_=()及构造函数Person()。其中name()对应java中的getter方法,name_=()对应java中的setter方法(由于JVM中不允许出现=,所以用$eq代替。值得注意的是定义的是公有成员,但生成的字节码中却是以私有的方式实现的,生成的getter、setter方法是公有的
因此,可以直接new操作创建Person对象

//默认已经有构建函数,所以可以直接newscala> val p=new Person()p: Person = Person@84c504//直接调用getter和setter方法//setter方法scala> p.name_=("john")//getter方法scala> p.nameres2: String = john//直接修改,但其实调用的是p.name_=("jonh")scala> p.name="jonh"p.name: String = jonh//getter方法scala> p.nameres28: String = jonh

你也可以定义自己的getter和setter方法

class Person{  //定义私有成员  private var privateName:String=null;  //getter方法  def name=privateName  //setter方法  def name_=(name:String){    this.privateName=name  }}
D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String privateName;  private java.lang.String privateName();  private void privateName_$eq(java.lang.String);  public java.lang.String name();  public void name_$eq(java.lang.String);  public cn.scala.xtwy.Person();}

从生成的字节码中可以看出:(1)定义成私有成员,其getter、setter方法也是私有的;(2)直接能访问的是我们自己定义的getter、setter方法。下面给出的是调用方式

scala> val p=new Person()p: Person = Person@12d0b54scala> p.nameres29: String = null//直接赋值法scala> p.name="john"p.name: String = johnscala> p.nameres30: String = john

从代码执行产生的结果,我们可以知道:通过p.name=“john”这种方式进行赋值,调用者并不需要知道是其通过方法调用还是字段访问来进行操作的,这便是著名的统一访问原则

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

class Person {  //类成员必须初始化,否则会报错  //这里定义的是一个val公有成员  val name:String="john"}D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private final java.lang.String name;  public java.lang.String name();  public cn.scala.xtwy.Person();}

从字节码文件中可以看出:val变量对应的是java中的final类型变量,只生成了getter方法

如果将成员域定义为private[this],则不会生成getter、setter方法

class Person {  //类成员必须初始化,否则会报错  //private[this]修饰  private[this] var name:String="john"}D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String name;  public cn.scala.xtwy.Person();}

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

class Person {  //类成员必须初始化,否则会报错  //@BeanProperty用于生成getXxx,setXxx方法  @BeanProperty var name:String="john"}D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String name;  public java.lang.String name();  public void name_$eq(java.lang.String);  public void setName(java.lang.String);  public java.lang.String getName();  public cn.scala.xtwy.Person();}

下图给出的是getter、setter方法产生的规则
这里写图片描述
来源:scala for the impatient

类主构造器

主构造器的定义与类的定义交织在一直,将构造器参数直接放在类名称之后,如下代码:

//下列代码不但定义了一个类Person,还定义了主构造器,主构造器的参数为String、Int类型class Person(val name:String,val age:Int)D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private final java.lang.String name;  private final int age;  public java.lang.String name();  public int age();  public cn.scala.xtwy.Person(java.lang.String, int);}//不难看出:上面的代码与下列java语言编写的代码等同public class Person{  private final String name;  private final int age;  public Person(String name,int age){       this.name=name;       this.age=age;  }  public String getName(){ return name}  public int getAge() {return age}}//具体使用操作如下:scala> val p=new Person("john",29)p: Person = Person@abdc0fscala> p.nameres31: String = johnscala> p.ageres32: Int = 29

主构造器会执行类定义中的所有语句,例如

//当在创建对象时,需要进行相关初始化操作时,可以将初始化语句放在类体中,同样也可以在类中添加或重写相关方法class Person(val name:String,val age:Int){  //println将作为主构建器中的一部分,在创建对象时被执行  println("constructing Person ........")  //重写toString()方法  override def toString()= name + ":"+ age}scala> val p=new Person("john",29)constructing Person ........p: Person = john:29

回过头来看的话,前面我们定义的Person类是一种无参主构建器

//Person类具有无参主构建器class Person {  println("constructing Person....")  val name:String="john"}scala> val p=new Person()constructing Person....p: Person = Person@79895f

主构建器还可以使用默认参数

//默认参数的主构建器class Person(val name:String="",val age:Int=18){  println("constructing Person ........")  override def toString()= name + ":"+ age}scala> val p=new Personconstructing Person ........p: Person = :18scala> val p=new Person("john")constructing Person ........p: Person = john:18

主构造器中的参数还可以加访问控制符

//默认参数的主构建器,参数带访问控制符号//age变成私有成员,其getter方法是私有的,外部不能访问class Person(val name:String="",private val age:Int=18){  println("constructing Person ........")  override def toString()= name + ":"+ age}

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

//不加变量修饰符class Person(name:String,age:Int){  println("constructing Person ........")  override def toString()= name + ":"+ age}D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private final java.lang.String name;  private final int age;  public java.lang.String toString();  public cn.scala.xtwy.Person(java.lang.String, int);}//与下面类定义等同class Person(private[this] val name:String,private[this] val age:Int){  println("constructing Person ........")  override def toString()= name + ":"+ age}D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private final java.lang.String name;  private final int age;  public java.lang.String toString();  public cn.scala.xtwy.Person(java.lang.String, int);}

值得注意的是,将上述Person类中的toString()方法去掉,则类中无任何地方使用了主构造器的参数,此时主构造器参数不会生成类成员

即将//不加变量修饰符class Person(name:String,age:Int){  println("constructing Person ........")  override def toString()= name + ":"+ age}改成:class Person( val name:String,age:Int){  println("constructing Person ........")}其字节码文件如下:D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  public cn.scala.xtwy.Person(java.lang.String, int);}//可以看出,主构造器参数不会生成类成员

下面图给出了Scala中主构建器参数生成类成员和方法时的规则
这里写图片描述
来源:scala for the impatient

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

//类名后面紧跟private关键字可以将主构建器设为私有,不允许外部使用class Person private(var name:String,var age:Int){  println("constructing Person ........")}//生成的字节码文件如下,可以看到其构建函数已经为private了D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String name;  private int age;  public java.lang.String name();  public void name_$eq(java.lang.String);  public int age();  public void age_$eq(int);  private cn.scala.xtwy.Person(java.lang.String, int);}//此时不能直接这么用scala> val p=new Person("john",19)<console>:9: error: constructor Person in class Person cannot be accessed in object $iw       val p=new Person("john",19)             ^

辅助构造函数

前面讲了,如果禁用掉了主构建器,则必须使用辅助构造函数来创建对象。辅助构造函数具有两个特点:(1)辅助构建器的名称为this,java中的辅助构造函数与类名相同,这常常会导致修改类名时出现不少问题,scala语言避免了这样的问题;(2)调用辅助构造函数时,必须先调用主构造函数或其它已经定义好的构造函数。

3.1 我们首先看一下只有辅助构造函数的Person类

//只有辅助构造函数的类class Person{  //类成员  private var name:String=null  private var age:Int=18  private var sex:Int=0  //辅助构造器  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:Int){    this(name,age)    this.sex=sex  }}//字节码文件D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String name;  private int age;  private int sex;  private java.lang.String name();  private void name_$eq(java.lang.String);  private int age();  private void age_$eq(int);  private int sex();  private void sex_$eq(int);  public cn.scala.xtwy.Person();  public cn.scala.xtwy.Person(java.lang.String);  public cn.scala.xtwy.Person(java.lang.String, int);  public cn.scala.xtwy.Person(java.lang.String, int, int);}//在定义辅助构造函数时,需要注意构造函数的顺序class Person{  //类成员  private var name:String=null  private var age:Int=18  private var sex:Int=0 //辅助构造器 def this(name:String,age:Int,sex:Int){    this(name,age)//此处会发生编译错误,这是因为def this(name:String,age:Int)没有被定义    this.sex=sex  }  def this(name:String){    this()    this.name=name  }  def this(name:String,age:Int){    this(name)    this.age=age  }}

3.2 带主构造函数、辅助构造函数的Person类

//具有主构建函数和辅助构建函数的Person类class Person(var name:String,var age:Int){  //类成员  private var sex:Int=0  //辅助构造器   def this(name:String,age:Int,sex:Int){    this(name,age)    this.sex=sex  }}生成的字节码文件如下:D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String name;  private int age;  private int sex;  public java.lang.String name();  public void name_$eq(java.lang.String);  public int age();  public void age_$eq(int);  private int sex();  private void sex_$eq(int);  public cn.scala.xtwy.Person(java.lang.String, int);  public cn.scala.xtwy.Person(java.lang.String, int, int);}

在主构造函数小节当中我们提到,有时候可能会禁用掉主构造函数,此时只能通过辅助构造函数来创建对象

//禁用主构造函数class Person private(var name:String,var age:Int){  //类成员  private var sex:Int=0  //辅助构造器   def this(name:String,age:Int,sex:Int){    this(name,age)    this.sex=sex   }}//其字节码文件内容如下D:\ScalaWorkspace\ScalaChapter06\bin\cn\scala\xtwy>javap -private Person警告: 二进制文件Person包含cn.scala.xtwy.PersonCompiled from "Person.scala"public class cn.scala.xtwy.Person {  private java.lang.String name;  private int age;  private int sex;  public java.lang.String name();  public void name_$eq(java.lang.String);  public int age();  public void age_$eq(int);  private int sex();  private void sex_$eq(int);  private cn.scala.xtwy.Person(java.lang.String, int);  public cn.scala.xtwy.Person(java.lang.String, int, int);}

添加公众微信号,可以了解更多最新Spark、Scala相关技术资讯
这里写图片描述

1 0