OCA/OCP Java Note (10): Class Design (2)

3. Implementing Interfaces

3.1. Defining an Interface

  1. 接口不能直接实例化。
  2. 一个接口中可以不包含任何方法。
  3. 接口不能标记为final。
  4. 顶层接口默认具有public或default的访问限制和abstract修饰符,将接口标记为private/protected/final会导致编译错误。此条不适用于内部接口。
  5. 接口中的所有非default方法默认具有abstract和public修饰符,将方法标记为private/protected/final会导致编译错误。关于default方法,可以参考这里

eg. 下面的两个接口的定义是等效的,编译器会把第一种自动转换为第二种形式。

public interface CanFly {
void fly(int speed);
abstract void takeoff();
public abstract double dive();
}
public abstract interface CanFly {
public abstract void fly(int speed);
public abstract void takeoff();
public abstract double dive();
}
public interface CanFly { void fly(int speed); abstract void takeoff(); public abstract double dive(); } public abstract interface CanFly { public abstract void fly(int speed); public abstract void takeoff(); public abstract double dive(); }
public interface CanFly {
    void fly(int speed);
    abstract void takeoff();
    public abstract double dive();
}
public abstract interface CanFly {
    public abstract void fly(int speed);
    public abstract void takeoff();
    public abstract double dive();
}

3.2. Inheriting an Interface

  继承借口的规则为:

  1. 接口或抽象类可以继承接口,该接口或抽象子类会继承所有抽象方法,作为其自己的抽象方法。
  2. 实现接口(或继承了接口的抽象类)的直接具体子类,必须实现其继承的所有抽象方法。

  接口可以使用extends继承多个接口。抽象类可以使用implements实现多个接口,而不必给出实际的实现。

eg. 

public interface HasTail {
public int getTailLength();
}
public interface HasWhiskers {
public int getNumberOfWhiskers();
}
public interface Seal extends HasTail, HasWhiskers {
}
public abstract class HarborSeal implements HasTail, HasWhiskers {
}
public class LeopardSeal implements HasTail, HasWhiskers { // DOES NOT COMPILE
}
interface HarpSeal implements HasTail, HasWhiskers { // DOES NOT COMPILE
}
public interface HasTail { public int getTailLength(); } public interface HasWhiskers { public int getNumberOfWhiskers(); } public interface Seal extends HasTail, HasWhiskers { } public abstract class HarborSeal implements HasTail, HasWhiskers { } public class LeopardSeal implements HasTail, HasWhiskers { // DOES NOT COMPILE } interface HarpSeal implements HasTail, HasWhiskers { // DOES NOT COMPILE }
public interface HasTail {
    public int getTailLength();
}
public interface HasWhiskers {
    public int getNumberOfWhiskers();
}
public interface Seal extends HasTail, HasWhiskers {
}
public abstract class HarborSeal implements HasTail, HasWhiskers {
}
public class LeopardSeal implements HasTail, HasWhiskers {  // DOES NOT COMPILE
}
interface HarpSeal implements HasTail, HasWhiskers {        // DOES NOT COMPILE
}

3.2.1. Classes, Interfaces, and Keywords

  类可以实现接口,不能继承接口。接口可以继承接口,不能实现接口。

3.2.2. Abstract Methods and Multiple Inheritance

  如果一个具体类实现的两个接口中具有相同名称和签名的方法,具体类只需要给出一个实现。

eg. 

public interface Herbivore {
public void eatPlants();
}
public interface Omnivore {
public void eatPlants();
public void eatMeat();
}
public class Bear implements Herbivore, Omnivore {
public void eatMeat() {
System.out.println("Eating meat");
}
public void eatPlants() {
System.out.println("Eating plants");
}
}
public interface Herbivore { public void eatPlants(); } public interface Omnivore { public void eatPlants(); public void eatMeat(); } public class Bear implements Herbivore, Omnivore { public void eatMeat() { System.out.println("Eating meat"); } public void eatPlants() { System.out.println("Eating plants"); } }
public interface Herbivore {
    public void eatPlants();
}
public interface Omnivore {
    public void eatPlants();
    public void eatMeat();
}
public class Bear implements Herbivore, Omnivore {
    public void eatMeat() {
        System.out.println("Eating meat");
    }
    public void eatPlants() {
        System.out.println("Eating plants");
    }
}

接口Herbivore和Omnivore都定义了eatPlants() ,Bear实现了Herbivore和Omnivore,只需要给出一个eatPlants() 的实现即可。
  如果一个具体类实现的两个接口中具有相同名称、不同签名的方法,具体类需要分别实现,相当于方法重载。

eg. 

public interface Herbivore {
public int eatPlants(int quantity);
}
public interface Omnivore {
public void eatPlants();
}
public class Bear implements Herbivore, Omnivore {
public int eatPlants(int quantity) {
System.out.println("Eating plants: "+quantity);
return quantity;
}
public void eatPlants() {
System.out.println("Eating plants");
}
}
public interface Herbivore { public int eatPlants(int quantity); } public interface Omnivore { public void eatPlants(); } public class Bear implements Herbivore, Omnivore { public int eatPlants(int quantity) { System.out.println("Eating plants: "+quantity); return quantity; } public void eatPlants() { System.out.println("Eating plants"); } }
public interface Herbivore {
    public int eatPlants(int quantity);
}
public interface Omnivore {
    public void eatPlants();
}
public class Bear implements Herbivore, Omnivore {
    public int eatPlants(int quantity) {
        System.out.println("Eating plants: "+quantity);
        return quantity;
    }
public void eatPlants() {
    System.out.println("Eating plants");
    }
}

  如果两个接口中具有相同名称和签名、不同返回类型的方法,具体类不能同时实现这两个接口。Java不允许定义两个名称和签名相同、而返回类型不同的方法。

eg. 

public interface Herbivore {
public int eatPlants();
}
public interface Omnivore {
public void eatPlants();
}
public class Bear implements Herbivore, Omnivore {
public int eatPlants() { // DOES NOT COMPILE
System.out.println("Eating plants: 10");
return 10;
}
public void eatPlants() { // DOES NOT COMPILE
System.out.println("Eating plants");
}
}
public interface Herbivore { public int eatPlants(); } public interface Omnivore { public void eatPlants(); } public class Bear implements Herbivore, Omnivore { public int eatPlants() { // DOES NOT COMPILE System.out.println("Eating plants: 10"); return 10; } public void eatPlants() { // DOES NOT COMPILE System.out.println("Eating plants"); } }
public interface Herbivore {
    public int eatPlants();
}
public interface Omnivore {
   public void eatPlants();
}
public class Bear implements Herbivore, Omnivore {
   public int eatPlants() {   // DOES NOT COMPILE
      System.out.println("Eating plants: 10");
      return 10;
   }
   public void eatPlants() {  // DOES NOT COMPILE
      System.out.println("Eating plants");
   }
}

3.3. Interface Variables

  1. 接口中的变量是public、static和final的,将其标记为private或protected会导致编译错误。
  2. 由于接口中的变量是final的,变量必须在定义时赋值。

eg. 下面的两种定义是等效的,编译器会把第一种自动转换为第二种的形式。

public interface CanSwim {
int MAXIMUM_DEPTH = 100;
final static boolean UNDERWATER = true;
public static final String TYPE = "Submersible";
}
public interface CanSwim {
public static final int MAXIMUM_DEPTH = 100;
public static final boolean UNDERWATER = true;
public static final String TYPE = "Submersible";
}
public interface CanSwim { int MAXIMUM_DEPTH = 100; final static boolean UNDERWATER = true; public static final String TYPE = "Submersible"; } public interface CanSwim { public static final int MAXIMUM_DEPTH = 100; public static final boolean UNDERWATER = true; public static final String TYPE = "Submersible"; }
public interface CanSwim {
    int MAXIMUM_DEPTH = 100;
    final static boolean UNDERWATER = true;
    public static final String TYPE = "Submersible";
}
public interface CanSwim {
    public static final int MAXIMUM_DEPTH = 100;
    public static final boolean UNDERWATER = true;
    public static final String TYPE = "Submersible";
}

3.4. Default Interface Methods

  Java 8中引入了默认方法,可以在接口中使用default关键词为抽象方法提供默认实现。

  1. 默认方法只能在接口中声明,不能在类和抽象类中声明。
  2. 默认方法必须标记为default,且必须提供实现。
  3. 默认方法不是static、final、abstract的,实现接口的类可以直接使用或重写默认方法。
  4. 默认方法是public的,标记为private或protected将无法编译。

public interface IsWarmBlooded {
boolean hasScales();
public default double getTemperature() {
return 10.0;
}
}
public interface IsWarmBlooded { boolean hasScales(); public default double getTemperature() { return 10.0; } }
public interface IsWarmBlooded {
   boolean hasScales();
   public default double getTemperature() {
      return 10.0;
   }
}

3.4.1. Default Methods and Multiple Inheritance

  由于一个类可以实现多个接口,通过引入默认方法,基本上相当于引入了多继承。如果一个类实现的两个接口中,具有相同名称和签名的默认方法,将无法编译,因为Java不知道应该调用那个方法。

eg. 

public interface Walk {
public default int getSpeed() {
return 5;
}
}
public interface Run {
public default int getSpeed() {
return 10;
}
}
public class Cat implements Walk, Run { // DOES NOT COMPILE
public static void main(String[] args) {
System.out.println(new Cat().getSpeed());
}
}
public interface Walk { public default int getSpeed() { return 5; } } public interface Run { public default int getSpeed() { return 10; } } public class Cat implements Walk, Run { // DOES NOT COMPILE public static void main(String[] args) { System.out.println(new Cat().getSpeed()); } }
public interface Walk {
    public default int getSpeed() {
        return 5;
    }
}
public interface Run {
    public default int getSpeed() {
        return 10;
    }
}
public class Cat implements Walk, Run {  // DOES NOT COMPILE
    public static void main(String[] args) {
        System.out.println(new Cat().getSpeed());
    }
}

  一个例外是,如果类重写了两个接口中具有相同名称和签名的默认方法,Java将使用该重写的方法,可以顺利编译。

eg. 

public class Cat implements Walk, Run {
public int getSpeed() {
return 1;
}
public static void main(String[] args) {
System.out.println(new Cat().getSpeed()); // 1
}
}
public class Cat implements Walk, Run { public int getSpeed() { return 1; } public static void main(String[] args) { System.out.println(new Cat().getSpeed()); // 1 } }
public class Cat implements Walk, Run {
    public int getSpeed() {
        return 1;
    }
    public static void main(String[] args) {
        System.out.println(new Cat().getSpeed());  // 1
    }
}

3.5. Static Interface Methods

  Java 8支持在接口中通过static定义静态方法,接口中的静态方法的特性和类中的静态方法基本一致,唯一的区别是,接口中的静态方法不会被任何实现该接口的类继承,由此避免了多继承的问题,即便一个类实现的两个接口具有相同名称和签名的静态方法,由于静态方法不会被继承,不会有编译错误。需要注意:

  1. 接口中静态方法是public的,使用private或protected将无法编译。
  2. 必须使用接口名来引用其中的静态方法。

public interface Hop {
static int getJumpHeight() {
return 8;
}
}
public class Bunny implements Hop {
public void printDetails() {
System.out.println(Hop.getJumpHeight());
System.out.println(getJumpHeight()); // DOES NOT COMPILE
}
}
public interface Hop { static int getJumpHeight() { return 8; } } public class Bunny implements Hop { public void printDetails() { System.out.println(Hop.getJumpHeight()); System.out.println(getJumpHeight()); // DOES NOT COMPILE } }
public interface Hop {
    static int getJumpHeight() {
        return 8;
    }
}
public class Bunny implements Hop {
    public void printDetails() {
        System.out.println(Hop.getJumpHeight());
        System.out.println(getJumpHeight()); // DOES NOT COMPILE
    }
}

4. Understanding Polymorphism

4.1. Casting Objects

  转换对象类型的规则为:

  1. 将对象从子类转换为父类,不需要显式地转换。
  2. 将对象从父类转换为子类,需要显式地转换。
  3. 编译器不允许转换到无关的类型。
  4. 如果被转换的对象不是目标类型的实例,运行时会抛出异常,尽管编译可以通过。

eg. 

public class Rodent {
}
public class Capybara extends Rodent {
public static void main(String[] args) {
Rodent rodent = new Rodent();
Capybara capybara = (Capybara)rodent; // Throws ClassCastException at runtime
}
}
public class Rodent { } public class Capybara extends Rodent { public static void main(String[] args) { Rodent rodent = new Rodent(); Capybara capybara = (Capybara)rodent; // Throws ClassCastException at runtime } }
public class Rodent {
}
public class Capybara extends Rodent {
    public static void main(String[] args) {
        Rodent rodent = new Rodent();
        Capybara capybara = (Capybara)rodent;  // Throws ClassCastException at runtime
    }
}

将对象rodent 转换为其子类Capybara,抛出ClassCastException异常。转换时应该使用如下的方法确保rodent 是Capybara的实例:
if(rodent instanceof Capybara) {
Capybara capybara = (Capybara)rodent;
}
if(rodent instanceof Capybara) { Capybara capybara = (Capybara)rodent; }
if(rodent instanceof Capybara) {
    Capybara capybara = (Capybara)rodent;
}

4.2. Virtual Methods

  Java中所有非final、非static和非private的方法都可以看做是虚方法。

eg. 

public class Bird {
public String getName() {
return "Unknown";
}
public void displayInformation() {
System.out.println("The bird name is: "+getName());
}
}
public class Peacock extends Bird {
public String getName() {
return "Peacock";
}
public static void main(String[] args) {
Bird bird = new Peacock();
bird.displayInformation();
}
}
public class Bird { public String getName() { return "Unknown"; } public void displayInformation() { System.out.println("The bird name is: "+getName()); } } public class Peacock extends Bird { public String getName() { return "Peacock"; } public static void main(String[] args) { Bird bird = new Peacock(); bird.displayInformation(); } }
public class Bird {
    public String getName() {
        return "Unknown";
    }
    public void displayInformation() {
        System.out.println("The bird name is: "+getName());
    }
}
public class Peacock extends Bird {
    public String getName() {
        return "Peacock";
    }
    public static void main(String[] args) {
        Bird bird = new Peacock();
        bird.displayInformation(); 
    }
}

输出为:
The bird name is: Peacock
The bird name is: Peacock
The bird name is: Peacock

4.3. Polymorphic Parameters

  多态的一个重要应用是用来向方法传递子类或接口的实例。

eg.

public class Reptile {
public String getName() {
return "Reptile";
}
}
public class Alligator extends Reptile {
public String getName() {
return "Alligator";
}
}
public class Crocodile extends Reptile {
public String getName() {
return "Crocodile";
}
}
public class ZooWorker {
public static void feed(Reptile reptile) {
System.out.println("Feeding reptile "+reptile.getName());
}
public static void main(String[] args) {
feed(new Alligator());
feed(new Crocodile());
feed(new Reptile());
}
}
public class Reptile { public String getName() { return "Reptile"; } } public class Alligator extends Reptile { public String getName() { return "Alligator"; } } public class Crocodile extends Reptile { public String getName() { return "Crocodile"; } } public class ZooWorker { public static void feed(Reptile reptile) { System.out.println("Feeding reptile "+reptile.getName()); } public static void main(String[] args) { feed(new Alligator()); feed(new Crocodile()); feed(new Reptile()); } }
public class Reptile {
    public String getName() {
        return "Reptile";
    }
}
public class Alligator extends Reptile {
    public String getName() {
        return "Alligator";
    }
}
public class Crocodile extends Reptile {
    public String getName() {
        return "Crocodile";
    }
}
public class ZooWorker {
    public static void feed(Reptile reptile) {
        System.out.println("Feeding reptile "+reptile.getName());
    }
    public static void main(String[] args) {
        feed(new Alligator());
        feed(new Crocodile());
        feed(new Reptile());
    }
}

输出为:
Feeding: Alligator
Feeding: Crocodile
Feeding: Reptile
Feeding: Alligator Feeding: Crocodile Feeding: Reptile
Feeding: Alligator
Feeding: Crocodile
Feeding: Reptile