08_Scala_OOP中_复写字段_抽象_匿名子类
覆写字段
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可以是任何类。
- 点赞
- 收藏
- 关注作者
评论(0)