什么是接口
接口定义行为
比如我们想要开发一个海洋乐园游戏,当中所有东西都会游泳。
谈到会游的东西,第一个想到的就是鱼。
我们刚才学了继承。
也许可以定义Fish类中有个swim()的行为。
package base.interf.OceanWorld.v1;
public abstract class Fish {
protected String name;
public Fish(String name) {
this.name = name;
}
public String getName() {
return name;
}
public abstract void swim();
}
由于实际上每种鱼游泳方式不同,所以将swim()定义为abstract,因此Fish也是abstract。
接着定义小丑鱼继承鱼
package base.interf.OceanWorld.v1;
public class Anemonefish extends Fish {
public Anemonefish(String name) {
super(name);
}
@Override
public void swim() {
System.out.printf("小丑魚 %s 游泳%n", name);
}
}
我们还可以定义鲨鱼Shark类继承Fish、食人鱼Piranha继承Fish。
package base.interf.OceanWorld.v1;
public class Shark extends Fish {
public Shark(String name) {
super(name);
}
@Override
public void swim() {
System.out.printf("鯊魚 %s 游泳%n", name);
}
}
package base.interf.OceanWorld.v1;
public class Piranha extends Fish {
public Piranha(String name) {
super(name);
}
@Override
public void swim() {
System.out.printf("食人魚 %s 游泳%n", name);
}
}
然后,需求变了。老板说话了。
为什么都是鱼?人也会游泳啊!怎么没有?
于是你就再定义Human类继承Fish。
package base.interf.OceanWorld.v1;
public class Human extends Fish {
public Human(String name) {
super(name);
}
@Override
public void swim() {
System.out.printf("人 %s 游泳%n", name);
}
}
等一下,Human继承Fish?不会觉得很奇怪吗?
就目前为止,程序也可以执行,继承会有是一种(is-a)的关系,
所以Anemonefish是一种Fish,Shark是一种Fish,Piranha是一种Fish,如果你让Human继承Fish,那Human是一种Fish?
重新想一下需求,想要的是所有东西”都会“游泳”,而不是“某种东西”都会“游泳”。
“所有东西”都会“游泳”,代表了“游泳”这个“行为”可以被所有东西拥有,而不是“某种”东西专属。
对于“定义行为”,在Java中可以使用interface关键字定义。
package base.interf.OceanWorld.v2;
public interface Swimmer {
public void swim();
}
Fish类
package base.interf.OceanWorld.v2;
public abstract class Fish implements Swimmer {
protected String name;
public Fish(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public abstract void swim();
}
Anemonefish
package base.interf.OceanWorld.v2;
public class Anemonefish extends Fish {
public Anemonefish(String name) {
super(name);
}
@Override
public void swim() {
System.out.printf("小丑魚 %s 游泳%n", name);
}
}
Human
package base.interf.OceanWorld.v2;
public class Human implements Swimmer {
private String name;
public Human(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public void swim() {
System.out.printf("人 %s 游泳%n", name);
}
}
最终的类图
以Java的语意来说,继承会有“是一种”关系,操作接口则表示“拥有行为”,但不会有“是一种”的关系。Human与Submarine操作了Swimmer,所以都拥有Swimmer定义的行为,但它们没有继承Fish,所以它们不是一种鱼,这样的架构比较合理也较有弹性,可以应付一定程度的需求变化。
行为的多态
和继承的多态一样。我们直接来看代码。
package base.interf.OceanWorld.v3;
public class Ocean {
public static void doSwim(Swimmer swimmer) {
swimmer.swim();
}
public static void main(String[] args) {
doSwim(new Anemonefish("小鱼儿"));
doSwim(new Shark("沙克"));
doSwim(new Human("贾维斯"));
doSwim(new Submarine("唐山一号"));
}
}
解决需求变化
今天老板突发奇想,想把海洋乐园变为海空乐园,有的东西会游泳,有的东西会飞,有的东西会游也会飞。
仔细想想,有的东西会飞,但不限于某种东西才有“飞”这个行为。
有了前面的经验,我们可以使用interface定义了Flyer接口。
package base.interf.OceanWorld.v4;
public interface Flyer {
public abstract void fly();
}
假设有台海上飞机具有飞行的行为,也可以在海面上航行,可以定义Seaplane操作Swimmer与Flyer接口
package base.interf.OceanWorld.v4;
public class Seaplane implements Swimmer, Flyer {
private String name;
public Seaplane(String name) {
this.name = name;
}
@Override
public void fly() {
System.out.printf("海上飞机 %s 在飞%n", name);
}
@Override
public void swim() {
System.out.printf("海上飞机 %s 航行海面%n", name);
}
}
在Java中,类可以操作两个以上的类,也就是拥有两种以上的行为。
例如,Seaplane就同时拥有Swimmer与Flyer的行为。
如果是会游也会飞的飞鱼呢?飞鱼是一种鱼,可以继承Fish类,飞鱼会飞,可以操作Flyer接
package base.interf.OceanWorld.v4;
public class FlyingFish extends Fish implements Flyer {
public FlyingFish(String name) {
super(name);
}
@Override
public void swim() {
System.out.println("飞魚游泳");
}
@Override
public void fly() {
System.out.println("飞魚会飞");
}
}
在Java中,类可以同时继承某个类,并操作某些接口。例如,FlyingFish是一种鱼,也拥有Flyer的行为。如果现在要让所有会游的东西游泳。我们可以这么来写
package base.interf.OceanWorld.v4;
public class Ocean {
public static void doSwim(Swimmer swimmer) {
swimmer.swim();
}
public static void main(String[] args) {
doSwim(new Anemonefish("小鱼儿"));
doSwim(new Shark("沙克"));
doSwim(new Human("贾维斯"));
doSwim(new Submarine("唐山一号"));
doSwim(new Seaplane("空軍零號"));
doSwim(new FlyingFish("甚平"));
}
}
最后我们的程序如下
然而,
老板又开口了,
- 并非所有的人都会游泳,所以不再让Human操作Swimmer
- 只有游泳选手会游泳,游泳选手是一种人,并拥有Swimmer的行为
- 有的飞机只会飞,所以设计一个Airplane类作为Seaplane的父类,Airplane操作Flyer接口
- Seaplane会在海上航行,所以在继承Airplane之后,必须操作Swimmer接口
- 直升机就只会飞
- 水里的话,将浅海游泳与深海潜行分开好了
那后我们又开始修改代码。
接口语法细节
接口的默认
-
Java中,使用interface来定义抽象的行为外观,方法要声明为
public abstract
,无须且不能有方法定义。interface Action{ public abstract void excute(); }
-
接口中的方法一定是公开且抽象,且不能有操作,没有别的写法了。为了方便,也可以省略public abstract。
interface Action{ void excute(); }
那么问题来了,下面代码的执行结果如何
interface Action{ void excute(); } class SomeAction implements Action{ @Override void excute() { System.out.println("一些操作"); } } public class MainAction { public static void main(String[] args) { SomeAction someAction = new SomeAction(); someAction.excute(); } }
在interface中,可以定义常数。
interface Action { public static final int TEST_NUMBER = 123; void excute(); } // 使用 Action.TEST_NUMBER
-
Java8以后,可以定义default方法。
default void excutedefault() { System.out.println("默认的一些操作"); }
-
类可以操作两个以上的接口,如果有两个接口都定义了某方法,而操作两个接口的类会怎样吗?
package com.ripjava.interfaces;
interface Some {
void excetue();
void doSome();
}
interface Other {
void excetue();
void doOther();
}
public class Service implements Some, Other {
@Override
public void doOther() {
System.out.println("doOther");
}
@Override
public void excetue() {
System.out.println("excetue");
}
@Override
public void doSome() {
System.out.println("doSome");
}
public static void main(String[] args) {
Some some = new Service();
some.excetue();
Other other = new Service();
other.excetue();
}
}
在设计上,思考一下:Some与Other定义的execute()是否表示不同的行为?
-
如果表示不同的行为,那么Service在操作时,应该有不同的方法操作,那么Some与Other的execute()方法就得在名称上有所不同,Service在操作时才可以有两个不同的方法操作。
-
如果表示相同的行为,那可以定义一个父接口,在当中定义execute()方法,而Some与Other继承该接口,各自定义自己的doSome()与doOther()方法.
interface SAction { void excetue(); } interface SSome extends SAction{ void doSome(); } interface SOther extends SAction{ void excetue(); void doOther(); }
匿名类
在将集合排序的时候和大家说。
使用enum枚举常数
从JDK5之后新增了enum语法,可用于定义枚举常数。直接来看例子:
public enum Action {
STOP, RIGHT, LEFT, UP, DOWN
}
那么如何使用这个Action呢?可以用它来声明类型。
public class Game {
public static void play(Action action) {
switch(action) {
case STOP:
System.out.println("停止");
break;
case RIGHT:
System.out.println("向右");
break;
case LEFT:
System.out.println("向左");
break;
case UP:
System.out.println("向上");
break;
case DOWN:
System.out.println("向下");
break;
}
}
public static void main(String[] args) {
play(Action.RIGHT);
play(Action.UP);
}
}
这个是enum的基本用法,以后如果有机会的话,会给大家讲一下,高级的用法。