5 Scala类

简单类和无参方法

  • 一个简单的类
class Counter{
  //字段必须初始化
  private var value = 0
  //方法默认公有
  def increment(): Unit ={
    value+=1
  }
  def current()=value
}
//使用类构造对象
val myCounter=new Counter
myCounter.increment()
myCounter.increment()
println(myCounter.current)
  • 调用无参数的方法,可以写小括号,也可以不写
  • 如果方法定义时不带小括号,那么调用使只能不带括号
  • 建议:对于改值器方法(改变对象状态的方法),加上小括号;对于取值器(不会改变对象状态方法)去掉小括号不错

带getter和setter的属性

  • 一般不使用公有字段,防止一个字段被错误修改,使用setter方法设置不能被随便修改
  • 一对getter/setter方法通常被称为属性。
  • 对于一个公有字段age,scala会自动生成setter和getter方法,其中getter方法为age,setter方法为age_=,通过下面的例子可以看到。
  • 对于Person.scala,使用scalac Person.scala编译,使用javap -private Person查看字节码;=号被翻译成了$eq
  • Person.scala如下
class Person{
  var age=0
}
  • 字节码如下
Compiled from "Person.scala"
public class Person {
  private int age;
  public int age();
  public void age_$eq(int);
  public Person();
}
  • 自己重新定义getter和setter方法,私有的age重新起个名字,方法设置为age和age_=,通过age_=防止age被减小。
class Person2{
  private var privateAge=0
  def age=privateAge
  def age_=(newAge:Int): Unit ={
    if(newAge>privateAge){
      privateAge=newAge
    }
  }
}
  • 其字节码如下。可以看到,对于私有的privateAge也生成了私有的getter和setter方法。
Compiled from "Person2.scala"
public class Person2 {
  private int privateAge;
  private int privateAge();
  private void privateAge_$eq(int);
  public int age();
  public void age_$eq(int);
  public Person2();
}
  • 如果字段是私有的,那么getter和setter方法也是私有的
  • 如果字段是val,则只有getter方法生成
  • 如果不生成任何getter和setter,可以将字段声明为private[this]

只带getter属性

  • 将字段声明为val,只生成一个final修饰的私有字段和一个公有的getter方法。
  • 例如
//Person3.scala
class Person3{
  val name="aa"
}
//查看的字节码
Compiled from "Person3.scala"
public class Person3 {
  private final java.lang.String name;
  public java.lang.String name();
  public Person3();
  • var foo:自动合成getter和setter方法
  • val foo:自动合成一个getter方法
  • 不能实现只有setter没有getter
  • 自己定义foo和foo_=

对象私有字段

  • 默认情况下,同一个类的一个对象可以访问另一个对象的私有字段
class Counter{
  //字段必须初始化
  private var value :Int = 0
  //方法默认公有
  def increment(): Unit ={
    value+=1
  }
  def current():Int=value
  //可以访问另一个对象的私有字段value
  def isLess(other:Counter):Boolean = { value < other.value }
}
  • 更加严格的限制,不能让它访问别的对象的私有字段
  • 将字段的定义将private改为private[this]修饰,即将类私有字段转换为对象私有字段
private[this] var value :Int = 0
  • 对于类私有字段,scala会生成私有的getter和setter方法,对于对象私有字段,不会生成getter和setter方法。示例如下,对象私有的card字段没有生成getter和setter方法。
//Person4.scala
class Person4{
  private var age = 0
  private[this] var card = 0
}
//编译后
Compiled from "Person4.scala"
public class Person4 {
  private int age;
  private int card;
  private int age();
  private void age_$eq(int);
  public Person4();
}
  • 此外可以用private[类名]定义指定类的方法可以访问指定的字段。类名必须是当前定义的类,或者包含该类的外部类。
  • private决定了getter或setter是私有或公有,val决定了会不会生成setter方法。所有的字段全部为私有,getter或setter有私有也有公有。

Bean属性

  • scala生成的getter和setter方法不是java工具需要的
  • JavaBeans规范把java属性定义为一堆getFoo/setFoo方法,只读的只有getFoo方法。
  • 将scala字段标注为@BeanProperty
  • 首先导入import scala.beans.BeanProperty
  • 生成四个字段,age、age_=、getAge、setAge
//Person5.scala
import scala.beans.BeanProperty
class Person5{
  @BeanProperty var age = 0
}
//查看
Compiled from "Person5.scala"
public class Person5 {
  private int age;
  public int age();
  public void age_$eq(int);
  public void setAge(int);
  public int getAge();
  public Person5();
}

  • 总结一下
序号 scala字段 生成的方法 何时使用
1 val / var name 公有的name,name_=(对于var) 实现一个公开访问并背后是字段形式保存的属性
2 @BeanProperty val / var name 公有的name,name_=(对于var),getName,setName(对于var) 与JavaBeans互操作
3 private val / var name 私有的name,name_=(对于var) 字段的访问限制在本类的方法
4 private[this] val / var name 不生成 字段访问限制在同一个对象,不经常用到
5 private[类名] val / var name 依赖于具体实现 字段访问赋予外部类,不经常用到
  • 各种可能的情况查看一下
//Person6.scala
import scala.beans.BeanProperty
class Person6{
  var name1 = 0
  val name2 = 0
  private var name3 = 0
  private val name4 = 0
  @BeanProperty var name5 = 0
  @BeanProperty val name6 = 0
  private[this] var name7 = 0
  private[this] val name8 = 0
}
//字节码
Compiled from "Person6.scala"
public class Person6 {
  private int name1;
  private final int name2;
  private int name3;
  private final int name4;
  private int name5;
  private final int name6;
  private int name7;
  private final int name8;
  public int name1();
  public void name1_$eq(int);
  public int name2();
  private int name3();
  private void name3_$eq(int);
  private int name4();
  public int name5();
  public void name5_$eq(int);
  public void setName5(int);
  public int name6();
  public int getName5();
  public int getName6();
  public Person6();
  • 主构造器的字段需要JavaBeans的getter和setter方法
  • class Person(@BeanProperty var name:String)

辅助构造器

  • scala可以有任意多的构造器
  • 最重要的构造器:主构造器
  • 辅助构造器可以有很多
  • 辅助构造器的名称为this,(java或C++中构造器的名称和类名相同,修改类名时不方便)
  • 每一辅助构造器必须以一个对先前定义的其他辅助构造器或主构造器的调用开始
class Person{
  private var name = ""
  private var age = 0
  println("主构造器")
  //第一个辅助构造器
  def this(name:String){
    //调用主构造器
    this()
    println("执行第一辅助构造器")
    this.name=name
  }
  //第二个辅助构造器
  def this(name:String,age:Int){
    //调用第一个辅助构造器
    this(name)
    println("执行第二辅助构造器")
    this.age=age
  }
}

//调用
val p1 = new Person()
println("---")
val p2 = new Person("及时雨宋江")
println("------")
val p3 = new Person("玉麒麟卢俊义",43)
//输出的结果
主构造器
---
主构造器
执行第一辅助构造器
------
主构造器
执行第一辅助构造器
执行第二辅助构造器

主构造器

  • 如果一个类没有显示地定义主构造器,则自动拥有一个无参的主构造器this()
  • 定义主构造器:定义类的时候
  • 主构造器的参数直接放在类名之后
  • 主构造器的参数被编译成字段,其值被初始化成构造时传入的参数,如果没有被声明为val或var则只是传递参数,不会是字段
  • 如果不带val或var的参数至少被一个方法使用,将被升格为字段,其类型是private final,但是不会有getter方法,更不会有setter方法
  • 主构造器会执行类定义的所有语句,参考辅助构造器部分,类定义语句为代码块中除了辅助构造器的所有的都是
  • 主构造器的参数可以是表中的任意形态,例如 private var age:Int。
  • 如果想私有主构造器,可以在参数列表前面加入private关键字,这样就只能通过辅助构造器构造对象class Person private(val name:String,val age:Int)
主构造器参数 生成的字段/方法
name:String 如果有方法使用name,则变为对象私有字段 ,否则不生成字段。从不生成方法
private val / var name:String 私有字段,私有的getter、setter方法(对于var)
val / var name:String 私有字段,公有的getter、setter方法(对于var)
@BeanProperty val / var name:String 私有字段,公有的Scala版和JavaBeans版getter、setter方法
// Person3.scala
import scala.beans.BeanProperty
class Person3(val name1:Int,
              var name2:Int,
              name3:Int,
              name4:Int,
              private var name5:Int,
              private val name6:Int,
              @BeanProperty var name7:Int,
              @BeanProperty val name8:Int){
  def descrption =  "name3 is " +name3
}

//得到的方法和字段
//所有的字段全部为私有,getter或setter有私有也有公有。
Compiled from "Person3.scala"
public class Person3 {
  private final int name1;
  private int name2;
  private final int name3;
  private int name5;
  private final int name6;
  private int name7;
  private final int name8;
  public int name1();
  public int name2();
  public void name2_$eq(int);
  private int name5();
  private void name5_$eq(int);
  private int name6();
  public int name7();
  public void name7_$eq(int);
  public void setName7(int);
  public int name8();
  public java.lang.String descrption();
  public int getName7();
  public int getName8();
  public Person3(int, int, int, int, int, int, int, int);

嵌套类

  • 一个外部类的不同对象的内部类是不同的
class Network{
  class Member(val name:String){
    val contacts=new ArrayBuffer[Member]
  }

  private val members=new ArrayBuffer[Member]

  def join(name:String): Member ={
    val m = new Member(name)
    members += m
    m
  }
}

//
val chatter = new Network
val myFace = new Network
val fred = chatter.join("Fred")
val wilma = chatter.join("Wilma")
fred.contacts += wilma
val barney=myFace.join("Barney")
// fred.contacts += barney  //错误
  • 如果实现内部类可以相互访问,使用伴生对象,这样fred和barney就可以添加好友了。
class Network{
  private val members=new ArrayBuffer[Network.Member]

  def join(name:String): Network.Member ={
    val m = new Network.Member(name)
    members += m
    m
  }
}

object Network{
  class Member(val name:String){
    val contacts=new ArrayBuffer[Member]
  }
}
  • 类型投影的方式,将new ArrayBuffer[Member]改成new ArrayBuffer[Network#Member],其含义是任何Network的Member
  class Member(val name:String){
    val contacts=new ArrayBuffer[Network#Member]
  }
发布了57 篇原创文章 · 获赞 73 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/weixin_40450867/article/details/103438756