千家信息网

Scala2.11.7学习笔记(六)类和对象

发表于:2024-11-14 作者:千家信息网编辑
千家信息网最后更新 2024年11月14日,鲁春利的工作笔记,好记性不如烂笔头1、扩展类extends是Scala中实现继承的保留字;class week extends month{......}week类继承了month类所有非私有成员;w
千家信息网最后更新 2024年11月14日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.*/


0