CS2312 Lecture 6

Abstract, Interface and Polymorphism

Abstract Methods

Methods that do not have implementation (body)

To create an abstract method, just write the method declaration without the body and use the abstract keyword 
//Note that there is no body
public abstract void someMethod();

Abstract Class

An abstract class is a class that contains one or more abstract methods
An abstract class cannot instantiated
Another class (Concrete class) has to provide implementation of abstract methods
Concrete class has to implement all abstract methods of the abstract class in order to be used for instantiation 
Concrete class uses extends keyword
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!!!!!!
}
When a concrete class extends the LivingThing abstract class, it must implement the abstract method walk(), or else, that subclass will also become an abstract class, and therefore cannot be instantiated.
package abs;

public class Human extends LivingThing {
    public void walk(){
        System.out.println("Human walks...");
    }
}

When to use Abstract Methods & Abstract Class?

-Abstract methods
usually declared where two or more subclasses are expected to fulfill a similar role in different ways through different
implementations.
These subclasses extend the same Abstract class and provide different implementations for the abstract methods
- Abstract classes
are to define broad types of behaviors at the top of an object-oriented programming class hierarchy, and use its subclasses to provide implementation details of the abstract class.
 

接口是抽象类的一种特例

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

An interface in Java is special type
A class with only method signatures
  • Methods have no body
  • Can never create an instance of an interface
Classes can implement the 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

- Methods
All methods of an Interface are abstract methods
Some methods of an Abstract class are abstract methods(have abstract modifier)
- Fields
An interface can only define constants
Abstract class can have fields
- Inheritance
Interfaces have no direct inherited relationship with any particular class, they are defined independently
Interfaces themselves have inheritance relationship among themselves
 

Interface as a Type

When you define a new interface, you are defining a new reference type
You can use interface names anywhere you can use any other type name
If you define a reference variable whose type is an interface, any object you assign to it must be an instance of a class that implements the interface
Example:
//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

Interfaces and classes are both types(an interface can be used in places where a class can be used)
Interface and Class can both define methods

Interface vs. Class: Differences

The methods of an Interface are all abstract methods (cannot have bodies)
Cannot create an instance from an interface
An interface can only be implemented by classes or extended by other interfaces
public interface [InterfaceName] {
        //some methods without the body
}
To create a concrete class that implements an interface, use the implements keyword.
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);
    }

}
Line
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);
}
Relation

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
Relationship of an Interface to a Class
A concrete class can only extend one super class, but it can implement multiple Interfaces
All abstract methods of all interfaces have to be implemented by the concrete class

Inheritance Among Interfaces

Interfaces are not part of the class hierarchy
However, interfaces can have inheritance relationship among themselves

Problem & Solution of Rewriting an Existing Interface

When to use an Abstract Class over Interface?

- For non-abstract methods
  • you want to use them when you want to provide common implementation code for all sub-classes (Reducing the duplication)
- For abstract methods
  • 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
A concrete class can extend only one super class whether that super class is in the form of concrete class or abstract class

Inheritance or Interface?  

- Inheritance
When you want to promote code reuse
A subclass is a refinement of superclass
A class can only have one superclass
- Interface
More general contracts than inheritance (Comparable, Writeable, process message, process button-press..)
When you want to define a method contract
When you cannot find any reuse in the methods

Interfaces:

An interface type can be used just like any other type
  • return type of method
  • argument type of method
  • array of interface type
One cannot create an object of an interface
All methods of an interface are public

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());
        }

    }
}
BankAccount
package abs;

public interface MyComparable {
    public int compareTo(Object obj);
}
MyComparable
has-a: A class has reference to another class
is-a: A class inherits from another class
implements: A class defines the methods of an Interface
 

Polymorphism

Signatures 
In any programming language, a signature is what distinguishes one function or method from another.
In Java, two methods have to differ in their names or in the number or types of their parameters
foo(int i) and foo(int i, int j) are different
foo(int i) and foo(int k) are the same
foo(int i, double d) and foo(double d, int i) are different.
方法参数个数、类型、返回值类型不同,虽然方法名一样,"签名"也不同.
 
Polymorphism
Polymorphism means many (poly) shapes (morph)
In Java, polymorphism refers to the fact that you can have multiple methods with the same name in the same class
There are two kinds of polymorphism:
- Overloading
Two or more methods with different signatures
- Overriding
Replacing an inherited method with another having the same signature
 

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?

So you can use the same names for methods that do essentially the same thing
So you can supply defaults for the parameters:
int increment(int amount) {
    
    count = count + amount;
    
    return count;
}

int increment() {
    
    return increment(1);

}
So you can supply additional information:
void printResults() {
    
    System.out.println("total = " + total + ", average = " + average);

}

void printResult(String message) {
    
    System.out.println(message + ": ");
    
    printResults();

}
You may want to do "the same thing" with different kinds of data:
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 can "overload" constructors as well as methods.
One constructor can "call" another constructor in the same class, but there are special rules
  • 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
}
A common reason for overloading constructors is (as above) to provide default values for missing parameters

 

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
This is called shadowing—name in class Dog shadows name in class Animal
 

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
This is called overriding a method
Method print in Dog overrides method print in Animal
A subclass variable can shadow a superclass variable, and a subclass method can override a superclass method
 
How to override a method
Create a method in a subclass having the same signature as a method in a superclass (create a method in a subclass having the same name and the same number and types of parameters - Parameter names don’t matter, just their types)
Restrictions:
The return type must be the same
The overriding method cannot be more private than the method it overrides
 
Override
Can treat an object of a subclass as an object of its superclass (A reference variable of a superclass type can point to an object of its subclass)
Late binding or dynamic binding (run-time binding): Method to be executed is determined at execution time, not compile time

多态(Polymorphic)也叫"动态绑定"(Dynamic Binding)同时也叫"迟绑定/后链接"(Late Binding)

动态绑定是指在执行期间(而非编译期间)判断所引用对象的实际类型, 根据其实际类型调用其相应的方法. (根据运行时对象的类型来判断调用的方法)

(前链接(early binding), 将方法与方法的对象联系起来的链接)

Java中由于多态, 在编译的时候, 编译器仅仅知道一个引用, 无法知道调用哪个方法. 后链接解决了该问题,在运行时根据对象的类型来链接方法. Java中, 所有的方法都是后链接, 除非该方法声明为final方法. 因为final方法无法被重写, 所以编译器可以知道该方法属于哪个类型.

Polymorphism: to assign multiple meanings to the same method name
Implemented using late binding
Can declare a method of a class final using the keyword final
public final void doSomeThing(){
       //...
}
If a method of a class is declared final, it cannot be overridden with a new definition in a derived class.
Java does not use late binding for methods that are private, marked final, or static.
 
A reference variable of subclass type cannot automatically point to object of its superclass

- 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()
If x refers to a coin, calls Coin.getMeasure()
 
Called late binding (dynamic binding): resolved at runtime
Different from overloading - overloading is resolved by the compiler (early binding)
 
Why override a method?

 
Dynamic Binding
Different objects can invoke different method definitions using the same method name.
The type of object being referenced at the time of the method call determines which method is invoked. (not the type of reference that was declared)
 
An object reference to an ancestor class can refer to an object of a descendant class.
Employee e = new Employee();
Person p;
p = e;

However, you can invoke only a method in class Person with the variable p.

However, if a method is overridden in the class Employee, and variable p references an Employee object, then the method in class Employee is used.
The variable determines what methods can be used, but the type referenced by the object determines which definition of the method will be used.
To use a method name in the class Employee with an object named by the variable p of type Person, use a type cast.
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常量等元数据加载到方法区,这些在类被加载时就已经知道,不需对象的创建就能访问的,就是静态绑定的内容;需要等对象创建出来,使用时根据堆中的实例对象的类型才进行取用的就是动态绑定的内容。
动态绑定
 
toString()
It is almost always a good idea to override public String toString() to return something "meaningful" about the object
When debugging, it helps to be able to print objects
When you print objects with  System.out.print or System.out.println, they automatically call the objects toString() method
When you concatenate an object with a string, the object’s toString() method is automatically called
 
Equality

==操作比较的是两个变量的值是否相等, 对于引用型变量表示的是两个变量在堆中存储的地址是否相同, 即栈中的内容是否相同.

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对象
equals
Never use == to test equality of Strings or arrays or other objects
Use equals for Strings, java. util.Arrays.equals(a1, a2) for arrays

Summary

}You should overload a method when you want to do essentially the same thing, but with different parameters
}You should override an inherited method if you want to do something slightly different than in the superclass
}It’s almost always a good idea to override public void toString() -- it’s handy for debugging, and for many other reasons
}To test your own objects for equality, override public void equals(Object o)
}There are special methods (in java.util.Arrays) that you can use for testing array equality
}You should never intentionally shadow a variable
 
 
posted @ 2018-02-24 11:08  Charonnnnn  阅读(212)  评论(0编辑  收藏  举报