zoukankan      html  css  js  c++  java
  • Scala学习八——继承

    一.本章要点

    • extends,final关键字和Java一样
    • 重写方法时必须使用override
    • 只有主构造器可以调用超类的构造器
    • 可以重写字段

    二.扩展类

      Scala扩展类和Java一样(使用extends关键字),也可以将类声明为final让它不能被扩展,也可以将单个方法或字段声明为final,确保不能重写。

      注:Java中final是不可变的(相当于Scala中的val),但是Scala中是不能被扩展

    三.重写方法

       使用override修饰符:

    public class Person{
    override def toString=....
    }

      override的作用在多个常见情况下给出有用的错误提示,如:

        • 拼错重写的方法名
        • 在新方法中使用了错误的参数类型
        • 在超类中引用新的方法,但是这个方法与子类方法相抵触(易违约基类问题,超类的修改无法在不检查所有子类的前提下被验证)

      在Scala中调用超类的方法和Java完全一样,使用super关键字

    四.类型检查和转换

      isInstanceOf:测试某个对象是否属于某个给定的类,可以用isInstanceOf方法,如果为true就可以用asInstanceOf方法将引用转换为子类的引用。

      例:

    //如果p指向的是Employee类及其子类(比如Manager)的对象,则p.isInstanceOf[Employee]将会成功。
    //如果p是null,则p.isInstanceOf[Employee]返回false,p.asInstanceOf[Employee]将返回null
    //如果p不是一个Employee,则p.asInstanceOf[Employee]将抛出异常
    if(p.isInstanceOf[Employee]){
    val s=p.asInstanceOf[Employee] 
    
    }

      classOf(在scala.Predef中,可以自动导入):测试某个对象是某个类的对象但又不是其子类。

      例:

    if(p.getClass==classOf[Employee])

    五.受保护字段和方法

      将字段或方法声明为protected,这样的成员可以被任何子类访问,但不能从其他位置看到。

      注:与Java不同,protected的成员对于类所属的包而言,是不可见的;  

        Scala还停工了protected[this]的变体,把访问权限限定在当前的对象

    六.超类的构造

      辅助构造器永远不可能直接调用超类的构造器,子类的辅助构造器最终都会调用主构造器,只有主构造器可以调用超类的构造器。

      主类构造器和类定义交织在一起,调用超类构造器也是交织在一起。

      例:

    //Employee类的三个参数有两个传递给了超类
    class employee(name:String,age:Int,val salary:Double) extends Person(name,age)

      注:在Scala构造器中,不能调用super(params)[Java可以]

        Scala可以扩展Java类,这种情况必须调用Java类的某一个构造方法

    七.重写字段

      子类可以重写父类的字段。

      注:

        • def只能重写另一个def
        • val只能重写另一个val或不带参数的def
        • val只能重写另一个抽象的var

      

    八.匿名子类

      和Java一样,可以通过包含带有定义或重写代码块的方式创建一个匿名的子类,例:

    //这会创建一个结构类型的对象,可以作为参数类型的定义
    val alien=new Person("Fred"){
    
    def greeting="xxxxxxx"
    
    }
    
    def meet(p:Person(def greeting:String)){}

    九.抽象类

      和Java一样,可以用abstract关键字标记不能被实例化的类(通常因为某个或某几个方法没有被完整定义)。

      例:

     

    abstract class Employee(val name:String){
    def id:Int //没有方法体——抽象方法
    } 

       注:

        Scala中抽象方法不用使用abstract关键字,只需省略方法体就行;  

        只要类中有一个抽象方法,这个类就需要是抽象类,类必须声明abstract;  

        子类中重写超类的抽象方法时不需要使用override关键字

    十.抽象字段

      即没有初始值的字段。

      例:

    abstract class Person{
    val id:Int //没有初始化——带有抽象的getter方法的抽象字段
    var name:String //带有抽象的getter和setter方法
    
    }

      注:子类必须提供具体的字段;

        子类重写超类的字段不需要使用override

    //具体的id和name属性
    class Employee(var id:Int) extends Person{
    var name=""
    
    }
    //通过匿名类型定制抽象字段
    val fred=new Person{
    
    
    val id=1
    val name="Fred"
    }

    十一.构造顺序和提前定义

      在子类中重写val并且在超类中的构造器使用该值有可能出现问题(因为超类的构造器先于子类的构造器运行,根本原因来自Java语言的设定——允许在超类的构造方法中调用子类的方法),如:

      

    class Creature{
    val range:Int=10
    val env:Array[Int] = new Array[Int](range)
    
    }
    
    class Ant extends Creature{
    override val range=2
    
    }
    ......
    //长度为0
    val ant=new Ant
    println(ant.env.length)
    /××
    过程如下:
    1.Ant的构造器在构造自己之前先调用Creature的构造器;                 
    2.Creature的构造器把它的range设为10;
    3.Creature的构造器为了初始化env数组,调用range()取值器;
    4.该方法被重写以输出(还未初始化)Ant类的range字段值;
    5.range方法返回0(这是对象被分配空间时所有整型字段的初始值);
    6.env被设为长度为0的数组;
    7.Ant构造器继续执行,将其range设为2
    ××/

      注:因此构造器内不应该依赖val的值;

        解决方式:

          1.将val声明为final,这样很安全但并不灵活;

          2.在超类中将val声明为lazy,这样很安全但不高效;

          3.在子类中使用提前定义方法(可以在超类的构造器执行之前初始化子类的val字段),使用with关键字(提前定义的等号右侧只能引用之前已有的提前定义,而不能使用类中的其他字段或方法)

    class Ant extends{
    override val range=2
    
    }with Creature

        可以使用-Xcheckinit编译器标志调式构造顺序    

    十二.Scala继承等级

     Scala继承等级

      注:

        AnyRef是Java或.Net虚拟机Object类的同义词;

        AnyVal和AnyRef都扩展于Any类,而Any类是整个继承等级的根节点;

        Any类中定义了isInstanceOf,asInstanceOf方法,相等性判断,哈希码的方法;

        AnyVal没有追加任何方法,他只是一个标记;

        AnyRef追加了来自Object类的监视方法wait和notify/notifyAll,同时提供了synchronized方法;

        所有Scala类都实现ScalaObject这个标记接口,该接口没有任何方法;

        Null值的唯一实例是null;

        Nothing类型没有实例  

    十三.对象相等性

      Scala中AnyRef的eq方法检查两个引用是否指向同一个对象,AnyRef的equals方法调用eq。

      实现类的时候有需要时重写equals根据自己的逻辑判断,例:

      注:将方法定义为final,因为通常而言在子类中正确扩展相等性判断非常困难;

        确保定义的equals方法参数类型为Any ;

        再定义equals时,记得同时定义hashCode 

    final override def equals(other:Any){
    
    val that=other.asInstanceOf(Item)
    if(that==null) false
    else description==that.description&&price==that.price
    }

      

    十四.练习

  • 相关阅读:
    IOC注解开发与XML整合
    Spring的IOC注解开发入门2
    Spring的IOC注解开发入门1
    降维学习笔记
    决策树集成学习summary
    python shuffle
    为什么会突然难过
    保存为pickle
    gitlab被屏蔽问题
    selenium-历史(一)
  • 原文地址:https://www.cnblogs.com/lyq-biu/p/11951569.html
Copyright © 2011-2022 走看看