08_Scala_OOP中_复写字段_抽象_匿名子类

举报
alexsully 发表于 2021/04/08 18:35:44 2021/04/08
【摘要】 Java 复写字段(隐藏字段,子类无法继承,如要实现 必须强转或者子类对象指向父类引用) Java 动态绑定: 调用的是方法,则Jvm机会将该方法和对象的内存地址绑定 Scala: 属性重写,虽然看似属性,底层生产了get set方法,相当于调用子类的get 方法,动态绑定 Scala:匿名子类 重写后 使用

覆写字段

Java中 字段不能被重写 [隐藏字段代替了重写,想调用就子类对象指向父类引用,或者强转后调用]
 
1. 但对于同一个对象,用父类的引用去取值(字段),会取到父类的字段的值,用子类的引用去取值(字段),则取到子类字段的值。
2. 在实际的开发中,要尽量避免子类和父类使用相同的字段名,否则很容易引入一些不容易发现的bug

# Java动态绑定机制 将一个子类的对象地址,交给了一个A_JAVA(父类的)引用 --> java的动态绑定机制:
   1.如果调用的是方法,则Jvm机会将该方法和对象的内存地址绑定
   2.如果调用的是一个属性,则没有动态绑定机制,在哪里调用,就返回对应值

public class JavaDaynamicBind {
    public static void main(String[] args) {
        A_java a = new B_java(); //用父类的引用去取值(字段),会取到父类的字段的值
        B_java b = new B_java(); //用子类的引用去取值(字段),则取到子类字段的值
        System.out.println("A_java: "  + a.name  +" \t B_java: " +b.name); //A_java: AA 	 B_java: BB

        // 动态绑定机制 将一个子类的对象地址,交给了一个A_JAVA(父类的)引用 --> java的动态绑定机制:
        //1.如果调用的是方法,则Jvm机会将该方法和对象的内存地址绑定
        //2.如果调用的是一个属性,则没有动态绑定机制,在哪里调用,就返回对应值
        A_java obj = new B_java();
        A_java cbj = new C_java();

//        System.out.println(obj.sum()); //40 java的动态绑定 JVM 方法绑定对象地址 new B_java() 对象
//        System.out.println(obj.sum1()); //40 java的动态绑定 JVM 方法绑定对象地址
        System.out.println("*********");
        System.out.println(cbj.sum()); // 30 依旧调用 B_java的getI()
        System.out.println(cbj.sum1()); // 20 直接return i,调用了属性,在A_java里调用 就用 A_java 的 i 属性
    }
}


class  A_java  {
    public int i = 10;
    public String name = "AA";
    public int sum() {
        return getI() + 10;
    }
    public int sum1() {
        return i + 10;
    }
    public int getI() {
        return i;
    }
}

class  B_java extends  A_java {
    public int i = 20;
    public String name = "BB";
    public int sum() {
        return getI() + 20;
    }
    public int sum1() {
        return i + 20;
    }
    public int getI() {
        return i;
    }
}

class  C_java extends  A_java {
    public int i = 20;
    public int getI() {
        return i;
    }
}


Scala 由于 属性底层对应都是 方法(void name_$eq  name_),所以动态绑定可以 锁定相关对象

覆写字段的注意事项和细节
def只能重写另一个def(即:方法只能重写另一个方法)
val只能重写另一个val 属性 或 重写不带参数的def ; var只能重写另一个抽象的var属性 (abstract 底层是抽象方法,重写相当于重写抽象方法)

一个属性没有初始化,那么这个属性就是抽象属性
抽象属性在编译成字节码文件时,属性并不会声明,但是会自动生成抽象方法,所以类必须声明为抽象类
如果是覆写一个父类的抽象属性,那么override 关键字可省略 [父类的抽象属性,生成的是抽象方法,不涉及到方法重写的概念,因此override可省略]

 object ScalaFieldOverrideDetail {
  def main(args: Array[String]): Unit = {

    val b_scala = new B_scala2
    val a_scala: A_scala = new B_scala2

    //b_scala.age => b_scala.age()
    // 虽然是指向父类引用,但JVM 绑定的是对象的地址,指向了对象(子类) b_scala.age() 绑定到 new  B_scala 类的对象,
    // 看似实现了属性的复写,其实是对应底层动态绑定  .age()来实现的
    //a_scala.age => a_scala.age()(b_scala.age())
    println("a_scala: " + a_scala.age + "\t b_scala: " + b_scala.age)  // 20 20

    println(a_scala.aa3(200)) //400  调用对象的 aa3()方法,动态绑定
  }
}

class A_scala {
  val age: Int = 10   // 1 必须是val 不能是 var : mutable variable cannot be overridden
                      // 2 虽然是属性,但底层会生成:  public int age()

  def aa(): Int = {     //public int aa()
    return 10
  }

  def aa2(): String = {   //public int aa2()
    return "alex"
  }

  def aa3(a: Int) ={ println(a) }

  def aa4(): Int = {     //public int aa()
    return 10
  }

}

class B_scala2 extends A_scala{
  override  val age: Int =20  // 虽然是 属性,但底层会生成:  public int age()
  override def aa(): Int = super.aa()   //  public int aa(){return super.aa}
  override def aa2(): String = "bob"    //public String aa2()
  override def aa3(a: Int): Unit = {println(a*2)}
  override val aa4 : Int = 0   //重写不带参数的def 因为底层生成了 public aa4: Int =  { return this.aa4} 方法
}

//在a_abst中,有一个抽象的字段(属性)
//1. 抽象的字段(属性):就是没有初始化的字段(属性)
//2. 当一个类含有抽象属性时,则该类需要标记为abstract
//3. 对于抽象的属性,在底层不会生成对应的属性声明,而是生成两个对应的抽象方法(name name_$eq)
abstract  class a_abst{
  var name : String  //抽象  //  public abstract void name_$eq(Sting paramString) // public abstract String name()
  var age: Int = 10

}

class  b_imp_a extends  a_abst{
  //说明
  //1. 如果我们在子类中去重写父类的抽象属性,本质是实现了抽象方法
  //2. 因此这里我们可以写override ,也可以不写
  override var name: String = _    // public void name_$eq(String x$1){this.name = x$1}    public String name(){return this.name}
}



抽象类 & 语法
abstract关键字标记不能被实例化的类; 方法不用标记abstract,只要省掉方法体即可,抽象类可以拥有抽象字段,抽象字段/属性就是没有初始值的字段


Scala Details
1 抽象类不能被实例化 (除非动态实现了抽象类的所有方法)
2 抽象类可以没有abstract方法
3 类包含了抽象方法或者抽象属性,则必须声明为abstract
4 抽象方法不能有主体,不允许使用abstract修饰。
5 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,或者它自己也声明为abstract类。
6 抽象方法和抽象属性不能使用private、final 来修饰,因为这些关键字都是和重写/实现相违背的
7 抽象类中可以有实现的方法.
8 子类重写抽象方法不需要override,写上也不会错.

 def main(args: Array[String]): Unit = {

    //1 默认情况下,一个抽象类是不能实例化的,如必须实例化,需要实现所有抽象方法&熟悉
    val p1 = new Person {
      override var name: String = _
      override def fly(): Unit = println("fly")
    }
    p1.sayHi()
  }
}
//抽象类
//2 抽象类可以没有abstract方法
//3 类包含了抽象方法或者抽象属性,则必须声明为abstract
//4 抽象方法不能有主体,不允许使用abstract修饰。

abstract class Person{
  var name : String //抽象的字段
  var color : String = "black" //普通属性
  def fly() //抽象方法,不标记 abstract 且无方法体
  def sayHi():Unit = { println("sayHi")}  ///在抽象类中可以有实现的方法 (普通方法)
}

//5 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法和抽象属性,或者它自己也声明为abstract类
class Student extends Person {
  override var name: String = _
  override def fly(): Unit = {println("student fly")}
}
//6 抽象方法和抽象属性不能使用private、final 来修饰,因为这些关键字都是和重写/实现相违背的
//7 抽象类中可以有实现的方法.
//8 子类重写抽象方法不需要override,写上也不会错.


包含带有定义或重写的代码块的方式创建一个匿名的子类,目的:方便,用一次就拉倒

JAVA

    public static void main(String[] args) {
        // new A_JAVA() 匿名子类 实现抽象方法;  a_java是 匿名子类对应的实例
        A_JAVA a_java = new A_JAVA() {
            @Override
            public void JAVA() {
            }
        };
        a_java.JAVA();
    }
}

abstract class A_JAVA {
    abstract public void JAVA();
}

Scala

  def main(args: Array[String]): Unit = {
    // new B_SCALA 匿名类 抽象类不能被实例化 (除非动态实现了抽象类的所有方法)
    val bscala = new B_SCALA {
      override var name: String = _
      override def fly(): Unit = ???
    }
    bscala.fly()
  }
}

abstract class B_SCALA {
  var name: String
  def fly()
}


Scala继承层级图

subtype : 子类型;   implicit Conversion 隐式转换  ;  class hierarchy : 类层次

继承层级图小结

  • 在scala中,所有其他类都是AnyRef的子类,类似Java的Object
  • AnyVal和AnyRef都扩展自Any类。Any类是根节点
  • Any中定义了isInstanceOf、asInstanceOf方法,以及哈希方法等。
  • Null类型的唯一实例就是null对象。可以将null赋值给任何引用,但不能赋值给值类型的变量[案例演示]。
  • Nothing类型没有实例。它对于泛型结构是有用处的,举例:空列表Nil的类型是List[Nothing],它是List[T]的子类型,T可以是任何类。
【版权声明】本文为华为云社区用户翻译文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容, 举报邮箱:cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。