Scala学习(五)类

1.简单类和无参方法

Scala类最简单的形式看上去和Java或C++中的很类似:ide

class Counter{

    private var value = 0  // 你必须初始化字段

    def increment() { value += 1 }   // 方法默认是公有的

    def current() = value // 定义时有(),所以调用时加不加()均可以

}

在解释器中执行函数

在Scala中,类并不声明为public。Scala源文件能够包含多个类,全部这些类都具备公有可见性。工具

--------------------------------this

建立对象:spa

val myCounter = new Counter  // 或者new Counter()

myCounter.increment()

println(myCount.current)  // 当你调用无参方法时,你能够写上园括号,也能够不写

对于改值器方法,使用(),而对于取值器方法,去掉()是不错的风格。scala

上面的实例中:code

myCounter.increment() // 对改值器使用()

println(myCount.current)  // 对取值器不使用()

你能够经过以不带()的方式声明current来强制这种风格:对象

class Counter{

    ...

    def current = value  // 定义中不带(),所以调用时必定不能加()

}

完整实例:ip

2.带getter和setter的属性

Scala对每一个字段都提供getter和setter方法(自动生成不须要定义)。rem

class Count{

    var value= 0

}

咱们定义了一个共有字段age,对于私有字段而言,getter和setter方法也是私有的(因为私有字段的getter和setter方法也是私有,所以私有字段若是须要对其进行操做,须要本身从新定义getter和setter方法)。

在Scala中,getter和setter分别叫作value 和 value=。

任什么时候候咱们能够本身从新定义getter和setter方法,例如:

此次咱们将value变成私有的,并新添加了getter和setter方法 v 和 v_=

针对上面setter方法的调用,咱们并无用c.v_=(20)这样去作,而是c.v = 20 这两种写法是等效的。

将上面的Scala代码编译成class文件后查看

提示:Scala对每一个字段生成的getter和setter方法听上去有些恐怖。不过你能够控制这个过程。

  1. 若是字段是私有的,则getter和setter方法也是私有的。
  2. 若是字段是val,则只有getter方法生成。Scala会生成一个私有的final字段和一个getter方法。咱们在上面的Scala类中添加一个val age = 100;这个变量

    经过查看编译后的class文件咱们能够证实上面的结论,Scala这么作是为了保证val常量的只读性。
  3. 若是你不须要任何getter和setter,能够将字段声明为private[this]。

总结:实现属性时你能够有以下四个选择:

  1. var foo:Scala自动合成一个getter和一个setter。
  2. val foo:Scala自动合成一个getter。
  3. 由你来定义foo和foo_=方法。
  4. 由你来定义foo方法

注:在Scala总,你不能实现只写属性(即带有setter但不带有getter的属性)

3.对象私有字段

在Scala中,方法能够访问该类的全部对象的私有字段。例如:

class Count{
    private var value = 0;
    def increment(){ value += 1 }
    def isLess(other: Count) = value < other.value //能够访问另外一个对象的私有字段
}

之因此是合法的是由于other也是Count对象。Scala容许咱们定义更加严格的访问限制,经过private[this]这个修饰符实现。

private[this] var value= 0; // 这样该字段只能是本身的实例能访问。

对于类私有的字段,Scala会生成getter和setter方法。但碎语对象私有的字段,Scala根本不会生成getter或setter方法。

说明:Scala容许你将访问权限赋予指定的类。private[类名]修饰符能够定义仅有指定类的方法能够访问给定的字段。这里的类名必须是当前定义的类,或者是包含该类的外部类。

4.Bean属性

正如以前看到的,Scala对于你定义的字段提供了getter和setter方法。可是并不符合JavaBeans规范,规范要求把Java的属性定义为getFoo/setFoo方法。需对Java工具都依赖这样的习惯。

当你将Scala的字段用注解@BeanProperty标注时,你所期待的getter和setter方法将会自动生成:

import scala.beans.BeanProperty
class Count{
    @BeanProperty var name: String = _;
}

注:其中字段name后面的 _ 表示默认值

咱们来看下变异后的class文件

说明:若是你以主构造器参数(见第7节)的方式定义了某字段,而且你须要JavaBeans版的getter和setter方法,你能够这样来作:

class Person(@BeanProperty var name: String)

 5.构造器

和Java同样,Scala也能够有任意多的构造器。不过,Scala类有一个构造器比其余全部构造器都更为重要,他就是主构造器。除了主构造器以外,类还能够有不少辅助构造器。

--------------------------------

咱们首先讲辅助构造器:

  1. 辅助构造器的名称为this。
  2. 每个辅助构造器都必须以一个对先前已经定义的其余辅助构造器或主构造器的调用开始。
class Count {
    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
    }
}

6.主构造器

在Scala中,每一个类都有主构造器。主构造器并不以this方法定义,而是与类定义交织在一块儿。

    1.主构造器的参数直接放置在类名以后。   

class Count(name: String, age: Int) {   //此处定义主构造

        ....

    }

    2.一个类若是没有显示的定义主构造器则自动拥有一个无参的的主构造器。这样一个构造器仅仅是简单的执行类体中的全部语句而已。

    3.主构造器的参数会被编译成字段

    4.主构造器会执行类定义中的全部语句,下面的println语句是主构造器的一部分。每当有对象构造出来,上述代码将会执行。咱们能够理解为Scala将Java类中的构造函数和类定义结合到了一块儿。            

class Person(val name: String,var age){
            println("hello world")
            def description = name + "is" + age + "years old"

        }

    5.若是你以主构造器参数(见第7节)的方式定义了某字段,而且你须要JavaBeans版的getter和setter方法,你能够这样来作:

class Person(@BeanProperty var name: String)

    6.你一般能够经过在主构造函数中使用默认参数来避免过多的使用辅助构造器。例如:class Person(val name:String = "",val age: Int =0)

--------------------------------

主构造器的参数能够试任意种形态,他们有什么区别呢?,例如:

class Count(val name: String, var age: Int,private var add: String , id: Int) { }

咱们经过观察编译后的class文件来看他们之间的区别:

  1. val 、var、private修饰的主构造函数中的参数最后都会被便觉得类中的字段
  2. 若是不带val 或 var的参数至少被一个方法锁使用,它将升级为字段。

    class Count(val name: String,age: Int ) { 
        def out {print(age)}
    }

    咱们看到上述示例的age字段升级为了字段。

说明:若是你想主构造器变成私有,能够这样:

class Person private(val id: Int){...}

7.嵌套类

在Scala中你几乎能够在任何语法中内嵌任何语法结构。你能够在类中定义类,在函数中定义函数:

下面示例是一个类中定义类的示例:

import scala.collection.mutable.ArrayBuffer
class School{ 
    class Student(val name:String,var age:Int){
        val content = new ArrayBuffer[Student]
    }

    private val students = new ArrayBuffer[Student]

    def stu() = {
        var m = new Student("HYC",26)
        students += m
        m
    }
}

在Scala中,每一个实例中的嵌套类,就像它们本身的字段同样,所以同一个类的不一样实例,嵌套类也不是同一个类。

例如:

var school1 = new School();

var school2 = new School();

//这里school1.Student  与 school2.Student是不一样的类。

说明:这里和Java不一样,在Java中内部类从属于外部类,而且访问方式也不一样。Scala访问为new School.Student,而Java中为School.new Student。

因为上面的Scala内嵌类的特性,为此咱们须要注意一个问题,同一个类的不一样实例,嵌套类不能相互传递,由于他们是不一样的类。

例如:

var school1 = new School();

var school2 = new School();

var student1 = school1.stu;

var student2 = school2.stu;

student1.content += student2; //不能够这样作student1和student2 是两个不一样的对象!

这样的状况在逻辑上来说是说的通的,由于毕竟是两种类型。可是对于数字Java的同窗好像又说不通,毕竟都是Student类型,面对这种状况,咱们有两种解决方案:

  1. 采用伴生对象的方式。
  2. 增长类型投影School#Student,其含义是任何School类型的Student,例如:
    class School{ 
        class Student(val name:String,var age:Int){
            val content = new ArrayBuffer[School#Student]
        }
    
    }

     

嵌套类访问外部类:

Scala中你能够采用 外部类.this 的方式方位外部类的this引用,你也可使用以下语法创建一个指向外部引用的别名:

class Outer(val name:String){ outer =>
   class Insider(val name:String){
      val str = name + " Inside " + outer.name
   }
}
相关文章
相关标签/搜索