类和无参构造器
在Scala中,类并不用声明为public;
Scala源文件中可以包含多个类,所有这些类都具有公有可见性;
val修饰的变量(常量),值不能改变,只提供getter方法,没有setter方法;
var修饰的变量,值可以改变,对外提供getter、setter方法;
如果没有定义构造器,类会有一个默认的无参构造器;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package lagou.cn.part04class Person { var name = "lagou" var nickName: String = _ var numInt: Int = _ var numDouble: Double = _ var boolean: Boolean = _ val num = 30 var age = 20 var address: String = null private var hobby = "旅游" private [this ] val cardInfo = "10010" def hello (message: String ): Unit = { println(s"$message , $cardInfo " ) } def addNum (num1: Int , num2: Int ): Int = { num1 + num2 } } object ClassDemo { def main (args: Array [String ]): Unit = { val person = new Person () val person2 = new Person println(s"${person.nickName} ${person.numInt} ${person.numDouble} ${person.boolean} " ) person.age = 50 person.age_=(20 ) println(person.age) person.hello("hello" ) println(person.addNum(10 , 20 )) } }
自定义getter和setter方法
对于 Scala 类中的每一个属性,编译后会有一个私有的字段和相应的getter、setter方法生成。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package lagou.cn.part04class Dog { private var _leg = 0 def leg : Int = _leg def leg_= (newLeg: Int ): Unit = { _leg = newLeg } } object GetterAndSetterDemo { def main (args: Array [String ]): Unit = { val dog = new Dog dog.leg_=(4 ) println(dog.leg) } }
自定义变量的getter和setter方法需要遵循以下原则:
- 字段属性名以“_”作为前缀,如: _leg
- getter方法定义为:def leg = _leg
- setter方法定义为:def leg_=(newLeg: Int)
Bean属性
JavaBean规范把Java属性定义为一堆getter和setter方法。
类似于Java,当将Scala字段标注为 @BeanProperty时,getFoo和setFoo方法会自动生成。
使用@BeanProperty并不会影响Scala自己自动生成的getter和setter方法。
在使用时需要导入包scala.beans.BeanProperty
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package lagou.cn.part04import scala.beans.BeanProperty class Teacher { @BeanProperty var name: String = _ } object BeanDemo { def main (args: Array [String ]): Unit = { val teacher=new Teacher teacher.name="jacky" teacher.name_=("tom" ) println(teacher.name) teacher.setName("lisi" ) println(teacher.getName) } }
上述Teacher类中共生成了四个方法:
name: String
name_= (newValue: String): Unit
getName(): String
setName (newValue: String): Unit
构造器
如果没有定义构造器,Scala类中会有一个默认的无参构造器;
Scala当中类的构造器分为两种:主构造器和辅助构造器;
主构造器的定义与类的定义交织在一起,将主构造器的参数直接放在类名之后。
当主构造器的参数不用var或val修饰时,参数会生成类的私有val成员。
Scala中,所有的辅助构造器都必须调用另外一个构造器,另外一个构造器可以是辅助构造器,也可以是主构造器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package lagou.cn.part04class Animal (name: String , age: Int ) { println(name) println(age) println("=========================" ) var gender: String = "" def this (name: String , age: Int , gender: String ) { this (name, age) this .gender = gender } var color: String = "" def this (name: String , age: Int , gender: String , color: String ) { this (name, age, gender) this .color = color } } object ConstructorDemo { def main (args: Array [String ]): Unit = { val animal = new Animal ("狗蛋" , 4 ) val animal2 = new Animal ("旺才" , 3 , "雄性" ) val animal3 = new Animal ("小六" , 5 , "雄性" , "黑色" ) } }
对象
单例对象
Scala并没有提供Java那样的静态方法或静态字段;
可以采用object关键字实现单例对象,具备和Java静态方法同样的功能;
使用object语法结构【object是Scala中的一个关键字】达到静态方法和静态字段的目的;对象本质上可以拥有类的所有特性,除了不能提供构造器参数;
对于任何在Java中用单例对象的地方,在Scala中都可以用object实现:
作为存放工具函数或常量的地方
高效地共享单个不可变实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package lagou.cn.part04object Object { println("这是单例对象的代码!" ) def printInfo : Unit = { println("Hello Scala Object!" ) } } object ObjectDemo { def main (args: Array [String ]): Unit = { Object .printInfo Object .printInfo } }
结果:
1 2 3 这是单例对象的代码! Hello Scala Object! Hello Scala Object!
Scala中的单例对象具有如下特点:
1、创建单例对象不需要使用new关键字
2、object中只有无参构造器
3、主构造代码块只能执行一次,因为它是单例的
伴生类与伴生对象
当单例对象与某个类具有相同的名称时,它被称为这个类的“伴生对象”;
类和它的伴生对象必须存在于同一个文件中,而且可以相互访问私有成员(字段和方法);
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package lagou.cn.part04class ClassObject { private var name = "lagou" def printInfo : Unit = { println(ClassObject .num) println("Hello Object!" ) } } object ClassObject { private val num = 10 def main (args: Array [String ]): Unit = { val classObject = new ClassObject println(classObject.name) classObject.printInfo } }
应用程序对象
每个Scala应用程序都必须从一个对象的main方法开始,这个方法的类型为 Array[String] => Unit;
备注:main方法写在class中是没有意义的,在IDEA中这样的 class 连run的图标都不能显示除了main方法以外,也可以扩展App特质(trait)
1 2 3 4 5 6 object Hello extends App { if (args.length > 0 ) println(s"Hello World; args.length = ${args.length} " ) else println("Hello World" ) }
apply方法
object 中有一个非常重要的特殊方法 – apply方法;
apply方法通常定义在伴生对象中 ,目的是通过伴生类的构造函数功能,来实现伴生对象的构造函数功能;
通常我们会在类的伴生对象中定义apply方法,当遇到类名(参数1,…参数n)时apply方法会被调用 ;
在创建伴生对象或伴生类的对象时,通常不会使用new class/class() 的方式,而是直接使用class()隐式的调用伴生对象的 apply 方法 ,这样会让对象创建的更加简洁;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package lagou.cn.part04class Student (name: String , age: Int ) { private var gender: String = _ def sayHi (): Unit = { println(s"大家好,我是$name ,$gender 生" ) } } object Student { def apply (name: String , age: Int ): Student = new Student (name, age) def main (args: Array [String ]): Unit = { val student = Student ("jacky" , 30 ) student.gender = "男" student.sayHi() } }
问题:在Scala中实现工厂方法,让子类声明哪种对象应该被创建,保持对象创建在同一位置。例如,假设要创建Animal工厂,让其返回Cat和Dog类的实例,基于这个需求,通过实现Animal伴生对象的apply方法,工厂的使用者可以像这样创建新的Cat和Dog实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 abstract class Animal { def speak } class Dog extends Animal { override def speak : Unit = { println("woof" ) } } class Cat extends Animal { override def speak : Unit = { println("meow" ) } } object Animal { def apply (str: String ): Animal = { if (str == "dog" ) new Dog else new Cat } def main (args: Array [String ]): Unit = { val cat = Animal ("cat" ) cat.speak val dog = Animal ("dog" ) dog.speak } }