CS2312 Lecture 6
Abstract, Interface and Polymorphism
Abstract Methods
Methods that do not have implementation (body)
//Note that there is no body public abstract void someMethod();
Abstract Class
package abs; public abstract class LivingThing { public void breath(){ //Normal method System.out.println("Living Thing breathing..."); } public void eat(){ //Normal method System.out.println("Living Thing eating..."); } /* Abstract method walk() We want this method to be implemented by a Concrete class. */ public abstract void walk(); //Abstract method!!!!!! }
package abs; public class Human extends LivingThing { public void walk(){ System.out.println("Human walks..."); } }
When to use Abstract Methods & Abstract Class?
implementations.
接口是抽象类的一种特例
package abs; public abstract class Door { public abstract void open(); public abstract void close(); } //普遍门具有开和关的两个性质
package abs; public interface Ring { public void bilibili(); }
package abs; public class Door_Ring extends Door implements Ring { public void open(){ System.out.println("open...."); } public void close(){ System.out.println("close...."); } public void bilibili(){ System.out.println("bilibili...."); } }
package abs; public interface CatEye { public void see(); }
package abs; public class Door_Ring_Cayeye extends Door implements Ring { public void open(){ System.out.println("open...."); } public void close(){ System.out.println("close...."); } public void bilibili(){ System.out.println("bilibili...."); } public void see(){ System.out.println("see...."); } }
Interfaces
- Methods have no body
- Can never create an instance of an interface
- A contract: the class will implement all the methods of an interface definition
package abs; // Note that Interface contains just set of method // signatures without any implementations. // No need to say abstract modifier for each method // since it assumed. public interface Relation { public boolean isGreater( Object a, Object b); public boolean isLess( Object a, Object b); public boolean isEqual( Object a, Object b); }
Why do we use Interfaces?
- To reveal an object's programming interface (functionality of the object) without revealing its implementation.
-
This is the concept of encapsulation
-
The implementation can change without affecting the caller of the interface
- The caller does not need the implementation at the complie time
-
- To have unrelated classes implement similar methods(behaviors).
- To model multiple inheritance - A class can implement multiple interfaces while it can extend only one class
Interface vs. Abstract Class
Interface as a Type
//Let's say Person class implements PersonInterface interface //You can do Person p1 = new Person(); PersonInterface pi1 = p1; PersonInterface pi2 = new Person();
Interface vs Class: Commonality
Interface vs. Class: Differences
public interface [InterfaceName] { //some methods without the body }
package abs; public class Line implements Relation { private double x1; private double x2; private double y1; private double y2; public Line(double x1, double x2, double y1, double y2){ this.x1 = x1; this.x2 = x2; this.y1 = y1; this.y2 = y2; } public double getLength(){ double length = Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); return length; } public boolean isGreater(Object a, Object b){ double aLen = ((Line)a).getLength(); double bLen = ((Line)b).getLength(); return (aLen>bLen); } public boolean isLess(Object a, Object b){ double aLen = ((Line)a).getLength(); double bLen = ((Line)b).getLength(); return (aLen<bLen); } public boolean isEqual(Object a, Object b){ double aLen = ((Line)a).getLength(); double bLen = ((Line)b).getLength(); return (aLen==bLen); } }
package abs; public interface Relation { public boolean isGreater(Object a, Object b); public boolean isLess(Object a, Object b); public boolean isEqual(Object a, Object b); }
When your class tries to implement an interface, implement all the methods of that interface.
Implementing Class
- Implementing class can have its own methods
- Implementing class extend a single super class or abstract class
Inheritance Among Interfaces
Problem & Solution of Rewriting an Existing Interface
When to use an Abstract Class over Interface?
- you want to use them when you want to provide common implementation code for all sub-classes (Reducing the duplication)
- the motivation is the same with the ones in the interface - to impose a common behavior for all sub-classes without dictating how to implement it
Inheritance or Interface?
Interfaces:
- return type of method
- argument type of method
- array of interface type
Example:
package abs; public class BankAccount implements MyComparable{ private double balance; public BankAccount(double balance){ this.balance = balance; } public double getBalance() { return balance; } public int compareTo(Object obj){ if(obj instanceof BankAccount){ BankAccount ba = (BankAccount) obj; if (this.balance > ba.balance) return 1; else if (this.balance < ba.balance) return -1; else return 0; } else { System.out.println("ERROR"); System.exit(0); return 10000; } } public static void sort(MyComparable[] array) { for (int i = array.length; i >= 1; i--) { // find the maximum index in the array [0..i-1] int maxIndex = i - 1; for (int j = 0; j < i; j++) { if (array[j].compareTo(array[maxIndex]) == 1) maxIndex = j; } // Replace last element with maximum value indexed at maxIndex MyComparable temporary = array[i - 1]; array[i - 1] = array[maxIndex]; array[maxIndex] = temporary; } } public static void main(String args[]){ BankAccount[] bas = new BankAccount[5]; for(int i=0; i<5; i++) { bas[i] = new BankAccount((int)Math.floor(Math.random()*50+1)); } for (int i=0; i<bas.length;i++){ System.out.println((int)bas[i].getBalance()); } System.out.println("============"); sort(bas); for (int i=0; i<bas.length;i++){ System.out.println((int)bas[i].getBalance()); } } }
package abs; public interface MyComparable { public int compareTo(Object obj); }
Polymorphism
Overloading
class Test { public static void main(String args[]) { myPrint(5.0); myPrint(5); } static void myPrint(int i) { System.out.println("int i = " + i); } static void myPrint(double d) { // same name, different parameters System.out.println("double d = " + d); } } // int i = 5 // double d = 5.0
// Java uses the most specific method
Why overload a method?
int increment(int amount) { count = count + amount; return count; } int increment() { return increment(1); }
void printResults() { System.out.println("total = " + total + ", average = " + average); } void printResult(String message) { System.out.println(message + ": "); printResults(); }
class Student extends Person { ... void printInformation() { printPersonalInformation(); printGrades(); } } class Professor extends Person() { ... void printInformation() { printPersonalInformation(); printResearchInterests(); } }
// Java’s print and println methods are heavily overloaded
NOTE:
class Test { public static void main(String args[]) { double d; int i; d = 5; // legal i = 3.5; // illegal i = (int) 3.5; // legal } } // Widening is legal // Narrowing is illegal (unless you cast)
class Test { public static void main(String args[]) { myPrint(5); } static void myPrint(double d) { System.out.println("answer: "+d); } } // answer: 5.0 // Legal because parameter transmission is equivalent to assignment // myPrint(5) is like double d = 5; System.out.println(d); which is legal
// But
// myPrint(5.0) is like int i = 5; System.out.println(d); which is illegal
- You call the other constructor with the keyword this
- The call must be the very first thing the constructor does
Point(int x, int y) { this.x = x; this.y = y; sum = x + y; } Point() { this(0, 0); //first thing }
Shadowing
class Animal { String name = "Animal"; public static void main(String args[]) { Animal animal = new Animal(); Dog dog = new Dog(); System.out.println(animal.name + " " + dog.name); } } public class Dog extends Animal { String name = "Dog"; } // answer: // Animal Dog
Overriding
class Animal { public static void main(String args[]) { Animal animal = new Animal(); Dog dog = new Dog(); animal.print(); dog.print(); } void print() { System.out.println("Superclass Animal"); } } public class Dog extends Animal { void print() { System.out.println("Subclass Dog"); } } // answer: // Superclass Animal // Subclass Dog
多态(Polymorphic)也叫"动态绑定"(Dynamic Binding)同时也叫"迟绑定/后链接"(Late Binding)
动态绑定是指在执行期间(而非编译期间)判断所引用对象的实际类型, 根据其实际类型调用其相应的方法. (根据运行时对象的类型来判断调用的方法)
(前链接(early binding), 将方法与方法的对象联系起来的链接)
Java中由于多态, 在编译的时候, 编译器仅仅知道一个引用, 无法知道调用哪个方法. 后链接解决了该问题,在运行时根据对象的类型来链接方法. Java中, 所有的方法都是后链接, 除非该方法声明为final方法. 因为final方法无法被重写, 所以编译器可以知道该方法属于哪个类型.
public final void doSomeThing(){ //... }
- Use cast
// Put results casting operations in brackets when paired with access operators ((Circle)object).getArea() // this one will work (Circle)object.getArea() // this one won't work, cuz The access(dot) operator
// has higher precedence than type casting
- If can not be casted, it will throw a ClassCastException
- Use instanceof to avoid casting exceptions - null instanceof SomeClass will always give you false.
x is measurable:
If x refers to a bank account, calls BankAccount.getMeasure()Employee e = new Employee(); Person p; p = e;
However, you can invoke only a method in class Person with the variable p.
Employee e = (Employee)p;
e.setEmployeeNumber(5678);
一:绑定 把一个方法与其所在的类/对象 关联起来叫做方法的绑定。绑定分为静态绑定(前期绑定)和动态绑定(后期绑定)。 二:静态绑定 静态绑定(前期绑定)是指:在程序运行前就已经知道方法是属于那个类的,在编译的时候就可以连接到类的中,定位到这个方法。 在Java中,final、private、static修饰的方法以及构造函数都是静态绑定的,不需程序运行,不需具体的实例对象就可以知道这个方法的具体内容。 三:动态绑定 动态绑定(后期绑定)是指:在程序运行过程中,根据具体的实例对象才能具体确定是哪个方法。 动态绑定是多态性得以实现的重要因素,它通过方法表来实现:每个类被加载到虚拟机时,在方法区保存元数据,其中,包括一个叫做 方法表(method table)的东西,表中记录了这个类定义的方法的指针,每个表项指向一个具体的方法代码。如果这个类重写了父类中的某个方法,则对应表项指向新的代码实现处。从父类继承来的方法位于子类定义的方法的前面。 动态绑定语句的编译、运行原理:我们假设 Father ft=new Son(); ft.say(); Son继承自Father,重写了say()。 1:编译:我们知道,向上转型时,用父类引用执行子类对象,并可以用父类引用调用子类中重写了的同名方法。但是不能调用子类中新增的方法,为什么呢? 因为在代码的编译阶段,编译器通过 声明对象的类型(即引用本身的类型) 在方法区中该类型的方法表中查找匹配的方法(最佳匹配法:参数类型最接近的被调用),如果有则编译通过。(这里是根据声明的对象类型来查找的,所以此处是查找 Father类的方法表,而Father类方法表中是没有子类新增的方法的,所以不能调用。) 编译阶段是确保方法的存在性,保证程序能顺利、安全运行。 2:运行:我们又知道,ft.say()调用的是Son中的say(),这不就与上面说的,查找Father类的方法表的匹配方法矛盾了吗?不,这里就是动态绑定机制的真正体现。 上面编译阶段在 声明对象类型 的方法表中查找方法,只是为了安全地通过编译(也为了检验方法是否是存在的)。而在实际运行这条语句时,在执行 Father ft=new Son(); 这一句时创建了一个Son实例对象,然后在 ft.say() 调用方法时,JVM会把刚才的son对象压入操作数栈,用它来进行调用。而用实例对象进行方法调用的过程就是动态绑定:根据实例对象所属的类型去查找它的方法表,找到匹配的方法进行调用。我们知道,子类中如果重写了父类的方法,则方法表中同名表项会指向子类的方法代码;若无重写,则按照父类中的方法表顺序保存在子类方法表中。故此:动态绑定根据对象的类型的方法表查找方法是一定会匹配(因为编译时在父类方法表中以及查找并匹配成功了,说明方法是存在的。这也解释了为何向上转型时父类引用不能调用子类新增的方法:在父类方法表中必须先对这个方法的存在性进行检验,如果在运行时才检验就容易出危险——可能子类中也没有这个方法)。 四:区分 程序在JVM运行过程中,会把类的类型信息、static属性和方法、final常量等元数据加载到方法区,这些在类被加载时就已经知道,不需对象的创建就能访问的,就是静态绑定的内容;需要等对象创建出来,使用时根据堆中的实例对象的类型才进行取用的就是动态绑定的内容。
==操作比较的是两个变量的值是否相等, 对于引用型变量表示的是两个变量在堆中存储的地址是否相同, 即栈中的内容是否相同.
equals操作表示的两个变量是否是对同一个对象的引用, 即堆中的内容是否相同.
总结: ==比较的是2个对象的地址, 而equals比较的是2个对象的内容.
public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = "Monday"; if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); } } // answer: // s1 == s2 // 说明: s1 与 s2 引用同一个 String 对象 -- "Monday"
public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2"); } } // answer: // s1 != s2 // s1 equals s2 // 说明:s1 s2分别引用了两个"Monday"String对象