-- 定义一个简单的类
scala的类和java基本差不多,先定义各一个简单类演示:
//定义类,包含field以及方法 class HelloWorld{ private var name="leo" def sayHello(){println("Hello, "+name)} def getName= name } // 创建类的对象,并调用方法 val helloWorld= new HelloWorld helloWorld.sayHello() println(helloWorld.getName) // 也可以不加括号,如果定义方法时不带括号,则调用方法时不能带括号
代码展示:
以上就是定义了一个简单的类 ,在外部怎么使用类,怎么调用方法。
以下将详解类
-- 自定义getter与setter
//如果只是希望拥有简单的getter和setter方法,那么就按照scala提供的语法规则,根据需要为field选择合适的修饰符就好:var、var、private、private[this] // 但是如果希望能够自己对getter与setter进行控制,则可以自定义getter与setetr方法 // 自定义setter方法的时候一定要注意scala的语法限制,签名、=、参数间不能有空格 class Student{ private var myName="leo" def name="your name is "+myName def name_=(newValue:String){ // name_=中间不能有空格 print("you cannot edit your name!!!") } } val leo =new Student print(leo.name) leo.name="leo1"
-- 仅暴露field的getter方法
//如果你不希望field有setter方法,则可以定义为val ,但是此时就再也不能改变field的值了 // 但是如果希望能够仅仅暴露出一个getter方法,并且还能通过某些方法更改field的值,那么需要综合使用private以及自定义getter方法 //此时,由于field是private的,所以setter和gette都是private,对外界没有暴露;自己可以实现修改field的值的方法,自己可以覆盖getter方法 class Student{ private var myName="leo" def updateName(newName:String){ myName=newName //自己实现修改field的值 } def name="your name is "+myName }
-- private[this] 的使用
// 如果将field使用private来修饰,那么代表这个field是类私有的,在类的方法中,可以直接访问类的其他对象的private field //这种情况下,如果不希望field被其他对象访问到,那么可以使用private[this],意味着对私有的field ,只有本类对象内可以访问到。 class Student{ private var myAge=0 def age_=(newAge:Int){ if(newAge >0)myAge=newAge else println("Illegal age!") } def age =myAge def older(s:Student)={ myAge > s.myAge // 如果 myAge 设置为 private[this] var myAge ,则这里报错 } }
代码展示如下:
-- java风格的setter和getter方法
//Scala的getter和setter方法的命名与java是不同的,是field和field_=的方式 // 如果想让scala自动生成java风格的getter和setter方法,只要给field添加@BeanProperty注解即可 此时会生成4个方法,name:String 、name_=(newValue:String):Unit、getName():String ,setName(newValue:String):Unit import scala.beans.BeanProperty //import scala.reflect.BeanProperty class Student{ @BeanProperty var name:String=_ } // class Student(@BeanProperty var name:String) //或者使用主构造函数的方式 val s =new Student s.setName("leo") s.getName()
代码展示如下:
使用主构造函数的方式:
-- 辅助 构造函数
// Scala中可以给类定义多个辅助constructor,类似于java中的构造函数重载 // 辅助构造函数之间可以相互调用,而且必须第一行调用主constructor class Student{ private var name private var age def this(name:String){ this() this.name=name } def this(name:String,age:Int){ this() this.age=age } }
-- 主constructor
// 在scala中 ,主constructor是与类名放在一起的 ,与java不同 //而且类中,没有定义在任何方法或者是代码块之中的代码,就是主constructor的代码 class Student(val name:String ,val age:Int){ println("your name is " +name+", your age is "+ age) } // 主 constructor中还可以通过使用默认参数 ,来给参数默认的值 class Student(val name:String="leo",val age:Int=30){ println("your name is "+ name+", your age is " +age) } val s=new Student // 直接使用默认参数 // 如果主 constructor传入的参数什么修饰都没有,比如name:String ,那么如果类内部的方法使用到了,则会声明为private[this] name, ,否则没有该field ,就只能constructor代码使用而已 。
-- 内部类
// 在scala中,同样可以在类中定义内部类,但是与java不同的是,每个外部类的内部类都是不同的类。 import scala.collection.mutable.ArrayBuffer class A{ class Student(val name:String) val students=new ArrayBuffer[Student]() def getStudent(name:String)={ new Student(name) } } val a1 = new A val s1=a1.getStudent("leo") a1.students +=s1 val a2=new A val s2=a2.getStudent("leo") a1.students +=s2 //报错 error:type mismatch,a1.student和a2的student的实例不是同一个, 所以会报错
-- 面向对象编程之对象 object(scala中所特有的)
//object ,相当于class的单个实例,通常在里面放一些静态的field或者method // 第一次调用object的方法时,就会执行object的constructor,也就是 //object内部不在method中的代码,但是object不能定义接收参数的constructor // 注意: object的constructor只会在其第一次被调用时执行一次,以后再次调用就不会再执行constructor了 // object通常用于作为单例模式的实现,或者放class的静态成员,比如工具方法 object Person{ private var eyeNum=2 println("this Person object") def getEyeNum=eyeNum }
举例:
-- 伴生对象
//如果有一个class ,还有一个与class同名的object,那么就称这个object是class的伴生对象,class是object的伴生类 // 伴生类和伴生对象必须存放在一个.scala文件中 //伴生类和伴生对象 ,最大的特点就是可以互相访问private field class Person(val name:String,val age:Int){ def sayHello=println("Hi, "+name+", I guess you are "+age+" years old!"+", and usually you must have "+ Person.eyeNum+"eyes.") } object Person{ private val eyeNum =2 def getEyeNum =eyeNum }
-- 让object继承抽象类
// object的功能其实和class类似,除了不能定义接收参数的constructor之外 // object可以继承抽象类,并覆盖抽象类中的方法 abstract class Hello(var message:String){ //定义抽象类 def sayHello(name:String):Unit } object HelloImpl extends Hello("Hello"){ //继承抽象类,实现抽象方法 override def sayHello(name:String)={ println(message+"," +name) } } // 访问 HelloImpl.sayHello("leo")
用法如下:
-- apply方法
//object非常重要的一个特殊方法,就是apply方法 //通常在伴生对象中实现apply方法,并在其中实现构造伴生类的对象的功能 //而创建伴生类的对象时,通常不会使用new Class的方式,而是使用Class()的方式,隐式第调用伴生对象的apply方法,这样会让对象创建更加简洁 //比如,Array类的伴生对象的apply方法就实现了接收可变数量的参数,并创建一个Array对象的功能 val a=Array(1,2,3,4,5) // 比如,定义自己的伴生类和伴生对象。 class Person(val name:String) object Person{ def apply(name:String)=new Person(name) } val p1=new Person("leo") val p2=Person("leo") //更简洁
-- main方法
//如同java中,如果要运行一个程序,必须编写一个包含main方法类一样,在scala中如果要运行一个程序,就必须有一个main方法作为入口 //scala中的main方法定义为def main(args:Array[String]),而且 object HelloWorld{ def main(args:Array[String]){ println("Hello World!") } } //除了自己实现main方法外,还可以继承App Trait,然后将需要在main方法中运行的代码,直接作为object的constructor代码,而且用args可以接受传入的参数 object HelloWorld extends App{ if(args.length >0)println("hello," +args(0)) else println("Hello World!") } // 如果要运行上述代码,需要将其放入.scala文件,然后先使用scalac编译,再用scala执行 scalac HelloWorld.scala scala -Dscala.time HelloWorld // App Trait的工作原理为:App Trait继承自DelayedInit Trait ,scalac命令进行编译时,会把 // 继承 App Trait的object的constructor代码都放到DelayedInit Trait的delayedInit方法中执行 scala -Dscala.time HelloWorld 显示执行时间
-- 用object实现枚举功能
// scala没有直接提供类似于java中的Enum这样的枚举特性,如果要实现枚举,则需要用object继承Enumeration类,并调用Value方法来初始化枚举值 object Season extends Enumeration{ val SPRING,SUMMER,AUTUMN,WINTER=Value } // 还可以通过Value传入枚举值得id和name,通过id和toString可以获取,还可以通过id和name来查找枚举值 object Season extends Enumeration{ val SPARING=Value(0,"spring") val SUMMER=Value(1,"summer") val AUTUMN=Value(2,"autumn") val WINTER=Value(3,"winter") } Season(0) Season.withName("spring") // 使用枚举object.values可以遍历枚举值 for(ele <- Season.values)println(ele)
使用如下所示:
-- -- extends 关键字
// scala中,让子类继承父类,与java一样,也是使用extends关键字 //继承就代表,子类可以继承父类的field和method,然后子类可以在自己的内部放入父类所没有,子类特有的field和method,使用继承可以有效复用代码 //子类可以覆盖父类的field和method,但是如果父类用final修饰,field和method用final修饰,则该类是无法被继承的,field和method是无法被覆盖的 class Person{ private var name="leo" def getName=name } class Student extends Person{ private var score="A" def getScore=score }
代码展示:
-- override和super
//scala中,如果子类要覆盖一个父类中的非抽象方法, 则必须使用override关键字 //override关键字可以帮助我们尽早的发现代码中的错误,比如:override修饰的父类方法的方法名写错了,比如要覆盖的父类的参数写错了等,父类中一旦没有就会报错。 //此外,在子类覆盖父类的方法后,如果要用要调用父类的被覆盖的方法, 就可以使用super关键字,显式指定调用父类的方法。
-- -- override field
// scala中,子类可以覆盖父类的的val field,而且子类的val field还可以覆盖 // 父类的val field的getter方法,只要在子类中使用override关键字即可 class Person{ val name:String="Person" def age:Int=0 } class Student extends Person{ override val name:String="leo" override val age:Int=30 }
-- isInstanceOf 和asInstanceOf
//如果我们创建子类的对象,但是又将其赋予父类类型的变量,则在后续的程序中,我们又需要将父类类型的变量转换为 //子类类型的变量,该怎么做呢? // 首先,需要使用isInstanceOf判断对象是否是指定类的对象,如果是的话,则可以使用asInstanceOf将对象转为指定类型。 //如果对象为null ,则isInstanceOf一定返回false,asInstanceOf一定返回null】 //注意:如果没有用isInstanceOf先判断对象是否为指定类的实例,就直接使用asInstanceOf转换,则可能会抛出异常 class Person class Student extends Person val p:Person=new Student var s:Student=null if(p.isInstanceOf(Student)) s=p.asInstanceOf(Student)
-- getClass 和classOf
// isInstanceOf只能判断出对象是否是指定类型以及其子类的对象,而不能精确判断出,对象就是指定类的对象 //如果要精确地判断对象就是指定类的对象,那么就只能使用getClass和classOf了 //对象.getClass可以精确获取对象的类,classOf[类]可以精确获取类,然后使用==操作符即可判断 class Person class Student extends Person val p:Person=new Student p.isInstanceOf[Person] p.getClass == classOf[Person] // 精确判断 p.getClass ==classOf[Student]
-- 使用模式匹配进行类型判断
//实际开发中,比如spark源码中,大量的地方都是使用到模式匹配的方式进行类型的判断 // 这种方式更加简洁明了,而且代码的可维护性和扩展性非常高 // 使用模式匹配,功能性上来说,与isInstanceOf一样,也是判断主要是该类以及该类的子类的对象即可,不是精确判断的 class Person class Student extends Person val p:Person =new Studnet p match{ case per :Person => println("it's Person's object") case _ => println("unknown type ") }
-- protected
// 跟java一样,scala中同样可以使用protected关键字来修饰field和method,这样在子类中就不需要super关键字,直接可以访问field和method // 还可以使用protected[this],则只能在当前子类对象中访问父类的field和method,无法通过其他子类对象访问父类的method和field class Person{ protected var name:String="leo" protected[this]var hobby:String="game" } class Student extends Person{ def sayHello =println("Hello, "+name) def makeFriends(s:Student){ println("my hobby is "+ hobby+", your hobby is" + s.hobby) }
使用如下:
-- 调用父类的constructor
//Scala中,每个类可以有一个主constructor和任意多个辅助constructor,而每个辅助constructor的第一行 //都必须是调用其他辅助constructor或者是主constructor;因此子类的辅助constructor是一定不可能直接调用父类的constructor的 // 只能在子类的主constructor中调用父类的constructor,以下这种语法,就是通过子类的构造函数来调用父类的构造函数 //注意:如果是父类中接收的参数,比如name和age,子类中接收时,就不要用任何val或者var来修饰,否则会认为是子类要覆盖父类的field class Person(val name:String,val age:Int) class Student(name:String,age:Int,var score:Double) extends Person(name,age){ def this(name:String){ this(name,0,0)} def this(age:Int){ this("leo",age,0) } }
-- 匿名子类
// 在scala中,匿名子类是非常常见的, 而且非常强大.spark源码中也大量使用了这种匿名子类 // 匿名子类也就是说,可以定义一个类的没有名字的子类,并直接创建其对象,然后将对象的引用赋予一个变量,之后甚至可以将该子类的对象传递给其他函数。 class Person(protected val name:String){ def sayHello="Hello ,I'm "+ name } val p=new Person("leo"){ //匿名子类 override def sayHello="Hi, I'm "+ name } def greeting(p:Person){ println(p.sayHello) }
用法:
-- 抽象类
// 如果在父类中,有某些方法无法立即实现,而需要依赖不同的子类来覆盖,重写实现自己不同的方法实现。 // 此时可以将父类中的这些方法不给出具体的实现,只有方法签名,这种方法就是抽象方法 //而一个类中如果有一个抽象方法,那么类就必须用abstract声明为抽象类,不可以被实例化 // 在子类中覆盖抽象类的抽象方法时,不需要使用override关键字 abstract class Person(val name:String){ def sayHello:Unit } class Student(name:String) extends Person(name){ def sayHello:Unit=println("Hello , " + name) }
-- 抽象field
//如果在父类中定义了 field ,但是没有给出初始化值,则此field为抽象field //抽象field意味着,scala会根据自己的规则,为val或var类型的field生成对应的getter和setter方法,但是父类中是没有该field的 //子类必须覆盖field,以定义自己的具体的field,并且覆盖抽象field, 不需要使用override关键字 abstract class Person{ var name:String } class Student extends Person{ val name:String="leo" // 不需要用override关键字 }