Scala面向对象
类
定义类
//不用修饰符
class HelloWorld{
//定义一个私有的field
private var name = "CZM"
def fun(){
//这是一个带参数的方法
}
def getName = name
//定义一个不带参数的方法
创建对象
val helloWorld = new HelloWorld //括号可有可无
调用方法
//可以不写*()*
helloWorld.fun()
//不能写*()*
helloWorld.getName
getter和setter方法
- 定义不带private的var field Scala生成面向JVM的类时,会定义成private的字段,并提供Public的getter和setter (也就是说,你在用对象调用这个值的时候,底层转换为使用pubic的getter/setter方法访问)
- 定义带private的field,则生成的getter和setter方法也是private的,外部不能调用,只能通过暴露的方法得到
- 定义成val field时,只会生成getter方法
- 如果不希望生成getter和setter方法,则将field声明成privete[this]
//自动生成的getter和setter方法,通过 val var private private[this]
var name = "CZM"
/*自动生成如下代码
//getter
def name = name
//setter
def name_ = (new_name:String)
*/
//手动getter和setter
private var name= "CZM"
def name = "Your name is "+name //错误,因为这样相当于两次定义了name
class HelloWorld {
private var old_name= "CZM"
def name = "Your name is "+old_name //正确:old_name 作为类中的私有feild,仅仅提供私有的getter和setter方法,而name作为一个提供了public的getter和setter方法的feild ==> var name并重写了public的getter和setter
def name_=(new_name: String) { //需要注意的是这里的语法规则,**=号前后不允许有空格**
old_name = new_name
print("Your new name is " + old_name)
}
}
//调用getter和setter
var helloWorld = new HelloWorld()
helloWorld.name //geter
helloWorld.name_= "asd" //正儿八经setter
helloWorld.name = "asd" //新型setter,将name作为返回,买一送一
privete[this]的使用
class Student{
private var myAge = 0
//private[this] var myAge = 0 //调用getter方法会报错,即使定义了getter和setter方法
def age = myAge
def age_=(newValue:Int){
if (newValue<0){println("illegal gar!!")}
else{myAge = newValue}
}
def older(s:Student){
myAge > s.myAge
}
}
怀旧党专用,使用类JAVA的getter和setter方法
@BeanProperty var age = 0
@BeanProperty var age:Int = _ //使用占位符需要声明类型
//同时拥有两种调用方式
s1.getAge
s1.age
构造函数constructor
主constructor
- 主构造函数就是与类名在一起的构造函数,关键问题是,当主构造函数定义了之后,就不能调用无参构造函数了,也就是说,主构造函数定义了最少参数的构造函数,以后就只有更多参数的了
- 主constructor中定义的参数,如果被使用到,则会被声明成privat[this] var name
- 如果类中没有使用到这个参数,则不会被声明,并不会有这个feild
//当然,可以设置默认参数
class Student(name:String,age:Int){}
辅助constructor
class Student(){
private var name = ""
private var age = 0
def this(name:String){
this() //如果不相互调用,必须调用**主构造函数**,且要在**首行 == JAVA**
this.name = name
}
def this(name:String,age:Int){
this(name)
this.age = age
}
}
内部类
与JAVA不同的是,Scala中的外部类的对象的内部类都是不同的类
import scala.collection.mutable.ArrayBuffer
class Class {
class Student(name: String) {}
val students = new ArrayBuffer[Student]()
def getStudent(name: String) = {
new Student(name)
}
}
val c1 = new Class
val c2 = new Class
val s1 = c1.getStudent("CZM") //s1: c1.Student = Class$Student@1d3f8af1
val s2 = c2.getStudent("czm") //s2: c2.Student = Class$Student@73589106
c1.students += s1 //正确
c1.students += s2 //错误,每一个内部类都是属于对象的,而不是独立存在的
/*错误信息
Error:(21, 24) type mismatch;
found : c2.Student
required: c1.Student
c1.students += s2
*/
object对象
由于类中定义的feild都是属于对象的,并没有属于类的字段,在JAVA中使用Static定义的字段作为所有对象共用的类的字段,在scala中提供了object来存放。
- object 也有constructor(不属于方法的代码都是静态加载),只在第一次被调用时执行一次,不能定义带参数的constructor
- object通常用作单例模式(只有自己一个对象),或者作为伴生对象存放静态成员,静态工具方法
- 与class相比,object仅仅是不能定义带参数的constructor,其他都是一样的,能继承其他方法
//定义object对象
object Person { //不能有括号,不能定义带参数的constructor
private var name = ""
println("This is constructor!")
def getName = name
println("This is constructor too!")
}
Person.getName
//让object继承抽象类
abstract class Hello {
def syaHello(name: String): Unit
}
object HelloObject extends Hello {
override def syaHello(name: String): Unit = {
println("Implement from Hello, your name is " + name)
}
}
HelloObject.syaHello("CZM")
伴生对象和伴生类
- 在同一个.scala文件中。同名的class和object,称object为class的伴生对象,class称为object的伴生类
- 伴生对象和伴生类之间可以相互访问private的feild
- class中的一切属于对象
- object中的一切属于类
class Person(name: String, age: Int) {
def sayHello = {
println("Hello " + name + " you are " + age + " years old, and you must have " + Person.legNum + "legs") //需要加上Object名访问object对象变量(即使不是私有的)
}
}
object Person {
private var legNum = 2 def getLegNum = legNum }
val p1 = new Person("czm", 18)
p1.sayHello
Apply方法(允许重载?)
- apply方法中一般做什么?创建一个对象,并返回对象
- 为什么要Apply方法?让创建类对象来的更简单,直接使用 类名()即可
- 为什么要在伴生对象中创建apply方法?因为这是一个工具方法,不应该属于对象,应该用类名调用
class Person(name: String)
object Person {
def apply(name: String): Person = new Person(name)
}
Person("你好")
main方法
- 为什么要main方法?作为函数的入口
- 为什么要定义在object中?因为main是public static的,需要被JVM使用类名调用
方法1
//注意文件名与类名相同
object HelloWorld {
def main(args: Array[String]) {
if (args.length != 0) {
println("Hello " + args(0))
} else {
println("Hello World!")
}
}
}
方法2:继承APP Trait
继承APP Trait,然后将需要在main中运行的代码,直接在constructor中编写。直接使用args接收传入的参数黑人问号脸??:这里的参数是来自父类APP Trait的父类DelayedInit Trait的Main方法中的args
- 工作原理:APP Trait 继承自DelayedInit Trait 编译时会将object中的constructor中的代码放到DelayedInit Trait的delayedInit方法中执行。DelayedInit的main方法将会调用delayedInit方法(为了懒加载?)
object HelloWorld extends App{
if (args.length != 0) {
println("Hello " + args(0))
} else {
println("Hello World!")
}
}
使用object实现枚举
- 枚举是什么:枚举是一个特殊的类,就是固定的多个常量对象的集合
- Scala没有提供类似JAVA中的Enum这样的枚举特性,可以使用object继承Enumeration类,并且调用Value方法来初始化枚举值
- 调用时,直接使用Season(0),为什么,不会跟apply冲突吗?继承了Enumeration,Enumeration中的apply方法是final的,子类不允许再定义apply只能继承Enumeration的apply(详情亲看源码)
scala实现枚举
object Season extends Enumeration{
val SPRING,SUMMER,AUTUMN,WINTER = Value //Value方法继承于Enumeration
//可以自定义Value的元素
val SPRING = Value(0,"spring")
...
}
//调用
//Enumeration的apply方法返回一个Value对象
Season(0)
res0: Season.Value = SPRING
Season("spring")
java中的枚举
enum Weeday{
SUNDAY,MONDAY.TUESDAY,WEDNESDAY,THURSDAY,FRIDAY,SATURDAY;
}
//在底层
//创建了一个final的Weeday类继承Enum
//实例化各个字段为 public final static 的 Weekday 对象,存在数组$VALUES中
//定义了一个values方法,用于返回数组 //Weekday.values()
//定义了一个valueOf方法,用于返回 //Weekday.valueof("SUNDAY")
岑忠满的博客新站点
http://cenzm.xyz