Scala2.11.7学习笔记(六)类和对象
鲁春利的工作笔记,好记性不如烂笔头
1、扩展类
extends是Scala中实现继承的保留字;
class week extends month{......}
week类继承了month类所有非私有成员;
week类是month类的子类,month类是week类的超类;
子类能重写超类的成员(字段、方法);
class week(val num : Int) extends month(val num : Int) {......}
单例对象同样能从类中继承,与类的继承语法相同:object day extends week {......}
重写
Scala中使用override保留字进行方法、字段重写
class week extends month { override def firstday () {......}}
override保留字声明其后的字段或方法是对超类的重写,也可以写在类定义参数中。
class week (overrideval lastday : String) extends month(val lastday : String)
重新定义的字段和方法不可重写(override),方法不同(参数类型或个数)不可重写。
scala> class month { | def secondary(m : String) { | println("secondary is " + m); | } | }defined class month scala> scala> class week extends month { | override def secondary(m : String) { // 重写该方法 | println("secondary is " + m); | } | }defined class week
2、重写规则
重写def
用val:利用val能重写超类没有参数的方法;
用def:子类方法与超类成员重名;
用var:同时重写getter、setter方法,只重写getter方法报错。
重写val:
用val:子类的一个私有字段与超类的字段重名,getter方法重写超类的getter方法
重写var:
用var:当超类的var是抽象的才能被重写,否则超类的var都会被继承。
// 新建文件month.scala,内容为:abstract class month { val zero : Int; val one = 25; // 可在子类中用val重写 var two = 15; // 不可在子类中用var重写,因为不是抽象的 var three : Int; def firstday ; // 可在子类中用val重写 def now ; // 可在子类中用var重写 def now_ ; def lastday(m : Char) = {} // 可在子类中用def重写}// 通过scalac命令编译该文件D:\LuclAppServ\scala-SDK\source>scalac month.scalaD:\LuclAppServ\scala-SDK\source>javap.exe -private month.class// 通过javap命令查看生成的文件Compiled from "month.scala"public abstract class month { // val变量且被初始化了 private final int one; // var变量且被初始化了 private int two; // val变量但为抽象的,直接生成了getter方法 public abstract int zero(); // 只有getter方法 public int one(); // 同时具有getter和setter方法(=被转义为$eq) public int two(); public void two_$eq(int); // var变量但为抽象的,直接生成了getter和setter方法 public abstract int three(); public abstract void three_$eq(int); // 其他抽象的方法 public abstract void firstday(); public abstract void now(); public abstract void now_(); // 具有方法体,非抽象方法 public void lastday(char); // 构造函数 public month();}
通过IDE工具生成的week.scala代码如下
/** * @author lucl */class week extends month { overrideval zero : Int = 10; override var three : Int = 3; override def firstday : Unit = { println("method of firstday."); } override def now : Unit = { println("method of now."); } override def now_ : Unit = { println("method of now_."); } }object week { def main (args : Array[String]) { var w = new week(); println(w.zero + "\t" + w.now); }}
查看生成的week子类代码
D:\LuclAppServ\workspaces\scala\scalaproj\bin>javap -private week.classCompiled from "week.scala"public class week extends month { private final int zero; private int three; public int zero(); public int three(); public void three_$eq(int); public void firstday(); public void now(); public void now_(); public week(); public static void main(java.lang.String[]);}D:\LuclAppServ\workspaces\scala\scalaproj\bin>javap -private week$.classCompiled from "week.scala"public final class week$ { public static final week$ MODULE$; public static {}; public void main(java.lang.String[]); private week$();}
说明:
子类构造器运行在超类构造器之后,在超类的构造器调用的子类被重写后,返回值可能不正确。
/** * @author lucl */class A { val num = 31; val days = new Array[Int](num); println("When invoke Class A the length of days is " + days.length + "."); def dayLength = { println("Class A : the length of days is " + days.length + ".") }}/** * @author lucl */object B extends A { overrideval num = 7; def main (args : Array[String]) { dayLength; println("The finally value of num is " + num); }}
运行结果:
构造B对象前先执行A的构造器,num被初始化为31,days被初始化为Array数组;
Array数组初始化时需要调用num,但num被子类重写了,但此时B的构造器还未被调用,num被初始化为0,days被初始化为长度为0的数组;
A的构造器执行完毕,执行B的构造器,num被初始化为7,但此时A中days已初始化过不会再更新其初始化信息,days的数组长度为0。
解决方法:
将超类的val声明为final(不可再被子类重写);
将超类的val声明为lazy;
在子类中使用提前定义语法。
a. final
当A类中的num声明为final val num : Int = 7,则子类中不可再重写该字段;
b. lazy
/** * @author lucl */class A { lazy val num = 31; // 通过lazy标注 val days = new Array[Int](num); println("When invoke Class A the length of days is " + days.length + "."); def dayLength = { println("Class A : the length of days is " + days.length + ".") }}/** * @author lucl */object B extends A { override lazy val num = 7; def main (args : Array[String]) { dayLength; println("The finally value of num is " + num); }}
运行结果
c. 提前定义
把需要提前定义的语句块放在extends与超类之间,并后接with保留字。
class B extends {overrideval num = 7; } with A {......}
/** * @author lucl */object B extends {overrideval num = 7; } with A { def main (args : Array[String]) { dayLength; println("The finally value of num is " + num); }}
执行结果:
3、抽象类
不能被实例的类叫做抽象类,用保留字abstract标记;
抽象类的某个或某几个成员没有被完整定义,这些没有被完整定义的成员为抽象字段或方法。
/** * @author lucl */abstract class year { val name : Array[String]; // 抽象的val,带有一个抽象的getter方法 var num : Int; // 抽象的var,带有抽象的getter/setter方法 def sign; // 没有方法体/函数体,是一个抽象方法}
只要类中有任意一个抽象成员,必须使用abstract标记;
重写抽象方法、抽象字段不需要使用override保留字。
保护
当一个类不希望被集成、拓展时,可在类声明前加上final保留字
final class year {......}
当一个类的某些成员不希望被重写时,可在成员声明前加上final保留字
class year {final def sign {......}}
当超类中的某些成员需要被子类继承,又不想对子类以外成员可见时,在成员声明前加上protected保留字;
protected[this],将访问限定于当前对象(子类也不可访问),类似于private[this];
/** * @author lucl * 只要类中有一个成员是抽象的,则类就需要声明为抽象类 */abstract class Human { var name : String; // 抽象字段 def sleep() : String; // 抽象方法 def info (address : String);}/** * */abstract class Teacher (tname : String, age : Int) extends Human { println(tname + "\t" + age); override var name : String = tname; // 若将类声明为def sleep = "8 hours",在下面调用super.sleep()的位置会提示返回的为Unit override def sleep() : String = "8 hours."; def info (address : String);}/** * Worker继承Teacher时有两个参数name和age需要从Worker中传递参数 * Worker的的参数名字需要与Teacher中一致,否则IDE会提示错误 */class Worker(tname : String, age : Int, salary : Int) extends Teacher (tname, age) { override def info (address : String) { println(tname + "' home is in " + address); println(tname + "'s age is " + age + ", earn ¥" + salary + "."); } override def toString = { tname + " is a worker, sleep " + super.sleep; }}object AbstractClassOps { def main(args : Array[String]) { val w = new Worker ("zhangsan", 25, 3000); w.info("BeiJing"); println(w); }}/** zhangsan 25 zhangsan' home is in BeiJing zhangsan's age is 25, earn ¥3000. zhangsan is a worker, sleep 8 hours.*/
4、类的private属性
/** * @author lucl */// 默认是public类型的class Person { // age必须赋值,否则该类必须为abstract的 private var age : Int = 0; // 没有private修饰默认是public的 // 无参的方法可以省略(),调用时可以省略(); def increment() = age += 1; // 若声明的方法不带(),则调用时不可带() def current = age; // 类可以直接访问伴生对象的私有属性 def speak = Person.sayHello;}class Student { /** * 声明为private类型的参数,只能通过当前类的函数来访问 */ private var privateAge : Int = 0; // 仅限于类的当前实例对象访问,其他传入的对象(如下面的other)将不可访问private[this]修饰的变量 private [this] val name : String = ""; // 自定义的getter/setter方法 ,用来操作私有字段 def age = privateAge; def age_ (newAge : Int) { privateAge = newAge; } /** * this对象的使用,表示调用该方法的当前对象 */ def sameStudent (other : Student) = { // 上面的等号表示有返回结果,否则最后的true会提示: // a pure expression does nothing in statement position; // you may be omitting necessary parentheses if (this.privateAge != other.privateAge) { false; } /** * 此时通过other将无法访问该name对象 * value name is not a member of Student */ /*if (this.name != other.name) { false; }*/ true; } // 在student中则不可通过Person类直接访问sayHello方法,提示: // method sayHello in object Person cannot be accessed in object Person // def speak = Person.sayHello; // 但可以通过如下方式访问: var p = new Person; p.speak; // 这里会直接执行}/** * 对象为类的伴生对象,类为对象的伴生类 */object Person { def main (args : Array[String]) { var p = new Person(); println("age is : " + p.age); // 可以访问到私有属性 p.increment; println("age is : " + p.age); println("current is : " + p.current); // 带有()则提示"Int does not take parameters" // p.current(); val s = new Student(); // variable privateAge in class Student cannot be accessed in Student // s.privateAge; println(s.age); s.age_(20); println(s.age); } private def sayHello () { println("Singleton object Person to say."); }}/** // 输出结果 age is : 0 age is : 1 current is : 1 Singleton object Person to say. 0 20*/
5、嵌套类
Scala允许任何语法结构中嵌套任何语法结构,因此能在类中定义类,类似于Java中的内部类。
内部类中可以访问外部类的成员,利用外部类.this或指针实现。
scala> class HelloWorld {pointto => val value2 = "HelloWorld"; class HI { var value3 = HelloWorld.this.value2; var value4 = pointto.value2; }} scala> var one = new HelloWorld;one: HelloWorld = HelloWorld@4b134f33scala> one.value2;res52: String = HelloWorldscala> class Family (val hname : String, val wname : String) { class Husband (var name : String) { println("Husband is : " + name); } class Wife (var name : String) { println("Wife is " + name); } def info () { var husband = new Husband(hname); var wife = new Wife(wname); println("This family holds husband " + husband.name + ", wife " + wife.name); }}scala> val f = new Family("Tom", "Jerry");f: Family = Family@62de96e8scala> f.info();Husband is : TomWife is JerryThis family holds husband Tom, wife Jerry
Java内部类:内部类是属于外部类的;
/** * * @author lucl * Java内部类示例 * 说明:Java内部类是从属于外部类的 */public class JavaOuter { private String name; public JavaOuter (String name) { this.name = name; } /** * 内部类 */ // 一旦用static修饰内部类,它就变成静态内部类了。 class Inner { private String name; public Inner (String name) { this.name = name; } public void foo (Inner inner) { System.out.println("\t" + JavaOuter.this.name + "\t" + inner.name); } } public void foo () { System.out.println("Outer : " + this.name); } /** * @param args */ public static void main(String[] args) { JavaOuter outer1 = new JavaOuter("Spark"); JavaOuter outer2 = new JavaOuter("Hadoop"); JavaOuter.Inner inner1 = outer1.new Inner("scala"); JavaOuter.Inner inner2 = outer2.new Inner("java"); outer1.foo(); inner1.foo(inner1); outer2.foo(); inner2.foo(inner2); outer1.foo(); inner1.foo(inner2); // 在这里inner1可以调用inner2 }}/** // 输出结果 Outer : Spark Spark scala Outer : Hadoop Hadoop java Outer : Spark Spark java*/
Scala内部类:内部类是属于外部类的对象的;
/** * @author lucl * Scala内部类示例 * 说明:Scala内部类是从属于外部类的对象的 */class ScalaOuter(val name : String) {outer => /** * 内部类 */ class Inner (val name : String) { def foo (inner : Inner) { println("\t" + outer.name + "\t" + inner.name + "."); } } def foo () { println("Outer : " + outer.name); }}object OOPInScala { /** * main方法 */ def main (args : Array[String]) { val outer1 = new ScalaOuter ("Spark"); val outer2 = new ScalaOuter ("Hadoop"); val inner1 = new outer1.Inner("scala"); val inner2 = new outer2.Inner("java"); outer1.foo; inner1.foo(inner1); outer2.foo; inner2.foo(inner2); // 对于scala来说,这inner1调用foo方法传递参数时,是不可以将inner2作为参数传递的 // IDE提示:type mismatch; found : outer2.Inner required: outer1.Inner // inner1.foo(inner2); }}/** // 输出结果 Outer : Spark Spark scala. Outer : Hadoop Hadoop java.*/