scala之类详解

章节

5.类[     ⬆TOP](#start)

本章要点:
- 类中字段自动带有getter和setter方法
- 可以使用自定义getter/setter方法替换
- 用@BeanProperty注解来生成javaBeans的get/set方法
- 每个类都有一个主要的构造器,这个构造器和类定义“交织”再一起。它的参数直接成为类的字段。主构造器执行类体中所有的语句。
- 辅助构造器是可选的。它们叫做this

5.1 简单类和无参方法

class Counter{
    private var value = 0 //必须初始化字段
    def increment(){  //方法默认是公有的
        value += 1
    }
    def current() = value
}

在Scala中类并不声明为public,源文件可以包含多个类,所有这些类都具有公有可见性。

val myCounter = new Counter // 或 new counter()
myCounter.increment()
println(myCounter.current)

myCounter.current //可以加(),也可以不加.
建议:对于改值(改变对象状态的方法)使用(),对于取值,不加。
可以通过再取值方法上不带()的方式声明current,来强制这种风格。

class Counter{
    ...
    def current = value  //定义中不带()
}

5.2 带getter/setter属性
用的是var修饰的变量:

class Person{
var age = 0     //默认私有,并且有get/set方法
}

编译后:(或者用javap -private Person.class查看,-p表示显示包括私有的方法和属性)

public class Person {
    private int age = 0;  //默认私有,并且有get/set方法

    public int age() {
        return this.age;
    }

    public void age_$eq(int x$1) {   //方法名age_=,=被翻译成$eq,因为JVM中不允许方法名中出现=
        this.age = x$1;
    }

    public Person() {
    }
}

如果把上面age加上private,编译后:

public class Person {
    private int age = 0;

    private int age() {   //方法全部成了private
        return this.age;
    }

    private void age_$eq(int x$1) {
        this.age = x$1;
    }

    public Person() {
    }
}

如果是val声明的变量,则编译后是private final修饰,且没有set方法:

class Message{
val info = "ljh"   //如果字段是val,则只有getter方法被生成,且生成一个私有的final字段。
}

编译后:

public class Message {
    private final String info = "ljh";

    public String info() {
        return this.info;
    }

    public Message() {
    }
}

5.4 对象私有字段

class Counter{
    private var value = 0   
    def increment(){  
        value += 1
    }
    def current() = value
    def isLess(other: Counter) = value < other.value//可以访问另一个对象的私有字段
}

private[this] 定义更加严格的访问限制,只能访问到当前对象的value,并不经常用到。
private[类名],将访问权限赋予外部类,并不经常用到。
对于类私有的字段,Scala会生成私有的getter/setter.
对于对象的私有字段,Scala根本不会生成getter/setter.

class Counter{
    private[this] var value = 0   //加上this这种私有定义,那么只能在当前类里面访问到。
    def increment(){  
        value += 1
    }
    def current() = value
     def isLess(other: Counter) = value < other.value//报错,因为other是另一个对象了
}

5.5 Bean属性
Scala编译后并不是预期的getXXX和setXXX,如果要生成JavaBeans规范,需要把字段标注为@BeanProperty(用了这个注解后,不能用private修饰,否则报错: error: `BeanProperty’ annotation can be applied only to non-private fields,只能再非私有属性使用)

import scala.reflect.BeanProperty
class Person {
    @BeanProperty var name: String = _
}

编译后:name/getter/setter

class Person() extends scala.AnyRef {
  @scala.beans.BeanProperty
  var name : scala.Predef.String = { /* compiled code */ }
  def getName() : scala.Predef.String = { /* compiled code */ }
  def setName(x$1 : scala.Predef.String) : scala.Unit = { /* compiled code */ }
}

如果是val,编译后:name/getter

class Person() extends scala.AnyRef {
  @scala.beans.BeanProperty
  val name : scala.Predef.String = { /* compiled code */ }
  def getName() : scala.Predef.String = { /* compiled code */ }
}

5.6 辅助构造器
- 辅助构造器名字为this。(在java或c++中是和类名同名)
- 每一个辅助构造器都必须以一个对先前已定义的其他辅助构造器或主构造器的调用开始。

class Person {
    private var name = ""
    private var age = 0

    def this(name: String) {  //辅助构造器
        this()  //调用主构造器
        this.name = name
    }
    def this(name: String, age: Int) {
        this(name)  //必须调用一个其他构造器,或主构造器
        this.age = age
    }
}

5.7 主构造器
- 主构造器参数直接放置在类名之后。
- 主构造器会执行类定义中的所有语句。

带有默认参数的主构造器

class Person(val name: String = "",val age: Int = 0)
这段代码将声明并初始化如下字段:
val name: String
private var age: Int

构造参数也可以是普通的方法参数,不带val或var。
如果不带val或var参数至少要被一个方法使用,才会升格为字段,并且都是对象私有,等同于private[this] val 字段效果。
否则,该参数不被保存为字段,而仅仅是一个可以被主构造器中代码访问的普通参数。

针对主构造器参数生成的字段和方法:

主构造器参数 生成的字段/方法
name:String 对象私有字段。如果没有方法使用name,则没有该字段
private val/var name:String 私有字段,私有的getter/setter方法
val/var name: String 私有字段,公有的getter/setter方法
@BeanProperty val/var name:String 私有字段,JavaBeans版的getter/setter

如果想让主构造器变成私有,可以在类名后面放置private关键字:
class Person private(val id: Int) { … }
这样用户就必须通过辅助构造器来构造Person对象了。

5.8 嵌套类

import scala.collection.mutable.ArrayBuffer
class Network {
    class Member(val name: String){
        val contacts = new ArrayBuffer[Member]
    }

    private val members = new ArrayBuffer[Member]

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

Scala中,每个实例都有他自己的Member类,chatter.Member和myFace.Member是不同的2个类

这和java不同,java内部类从属于外部类
Scala构建一个新的内部对象:new chatter.Member
java构建:chatter.new Member()

val chatter = new Network
val myFace = new Network

val fred = chatter.join("Fred)
val wilma = chatter.jon("Wilma")
fred.contacts += wilma  // OK
val barney = myFace.join("Barney") //类型为myFace.Member
fred.contacts += barney //报错,不能将myFace.Member添加到chatter.Member元素缓冲当中

想要上面能成功,有2种解决方式:
1. 将Member类移动到别处,一个不错的位置是Network的伴生对象

object Network {
    class Member(val name: String){
        val contacts = new ArrayBuffer[Member]
    }
}
class Network {
    private val members = new ArrayBuffer[Network.Member]
    ...
}
  1. 可以使用类型投影Network#Member,含义是”任何Network的Member”。
    如果只想在某些地方,而不是所有地方,利用这个细粒度的“每个对象有自己的内部类”的特性,则可以考虑使用类型投影。
class Network {
    class Member(val name: String){
        val contacts = new ArrayBuffer[Network#Member]
    }
    ...
}

说明:再内嵌类中,你可以通过 外部类.this 的方式来访问外部类的this引用,建立一个指向该引用的别名. class Network { outer => 语法使得outer变量指向Network.this。outer可以是任意的名字,尽量不要用self,很常见,容易引发歧义。

class Network(val name: String) { outer =>   
    class Member(val name: String){  
        ...
        def description = name +"inside"+outer.name
    }  
}

[     ⬆TOP](#start)

猜你喜欢

转载自blog.csdn.net/mar_ljh/article/details/80074273