当某一抽象类中所有的函数都是抽象函数时,就可以将该抽象类用另外一个形式来表示,这个形式就是接口
接口的定义:定义普通的类或者抽象类可以使用class关键字,定义接口必须interface关键字完成
本篇以List为例,进行代码演示:
List是线性表的定义,对于线性表而言,它主要有两种实现方式:数组和链表。两者实现的具体方式有很大区别,但其具体的操作名称是一致的,最基本的就是对线性表中的任一位置进行增删查改的操作。
//抽象类
public abstract class List {
public abstract void add(int index,int element);
public abstract int remove(int index);
public abstract int get(int index);
public abstract int set(int index,int element);
}
//用接口来表示我们的抽象类
public interface List {
public abstract void add(int index,int element);
public abstract int remove(int index);
public abstract int get(int index);
public abstract int set(int index,int element);
}
接口中成员的特点
- 每一个函数都必须是public abstract类型。在定义函数时,这个两个关键字是可以忽略不写的,如以下代码所示:
public interface List {
public void add(int index,int element);
int remove(int index);
abstract int get(int index);
public abstract int set(int index,int element);
}
上述几个方法的定义都是正确的,虽然函数的定义不全,但接口会自动帮你补全。其中需要注意的是返回值类型、函数名、参数列表是必须保留的
一般而言,建议大家如下定义:
public interface List {
public void add(int index,int element);
public int remove(int index);
public int get(int index);
public int set(int index,int element);
}
以上是一些通用的定义:去掉abstract,保留public
保留public的原因:
接口的方法和抽象类一样,都需要被子类重写。子类重写父类的函数时,权限必须大于等于父类函数的权限。此处接口中所有的函数都是public的,那么子类中所有重写的函数也必须是public的
- 接口中可以定义变量,但是变量必须由固定的修饰符修饰:public static final,所以接口中的变量也称之为常量。
public interface List {
int num = 10; //public static final int num = 10; 其实就是一个不能修改的常量值
public void add(int index,int element);
public int remove(int index);
public int get(int index);
public int set(int index,int element);
}
可以看出,上述接口的内容和一般的类很像:
int num = 10; 可能被误认为是成员变量
public void add(int index,int element); 可能被误认为是成员函数
- 和抽象类一样,接口是不能直接创建对象的
- 子类必须覆盖掉接口中所有的抽象方法后才可以实例化,否则子类是一个抽象类
单继承的弊端:某一个类(linkedList) 它可能同时具备了多个父类(List Stack Queue)的定义。
- 接口的出现解决了上述单继承的弊端,通过多实现完成多继承这种机制。
public abstract class List {
public abstract void add(int index,int element);
public abstract int remove(int index);
public abstract int get(int index);
public abstract int set(int index,int element);
}
//ArrayList是底层由数组实现的线性表
public class ArrayList extends List {} //可以,ArrayList单继承
//对于linkedList而言 除了有线性表的基本操作外 还具有栈、队列、双端队列之类的操作行为
public abstract class Stack {
public abstract int pop();
public abstract void push(int element);
}
public abstract class Queue {
public abstract int poll();
public abstract void offer(int element);
}
//如何解决?将Stack和Queue变为接口并让linkedList实现,这样也没有改变linkedList继承List这个事实
public interface Stack {
public int pop();
public void push(int element);
}
public interface Queue {
public int poll();
public void offer(int element);
}
public class linkedList extends List implements Stack,Queue {
//必须重写List继承来的抽象函数 重写Stack和Queue接口的抽象函数
//如果但凡有一个没有重写 那么该linkedList就必须声明为抽象类
}
多继承的弊端:多继承时,当多个父类中有相同功能时,子类调用会产生不确定性。
- 接口通过多实现implements解决多继承弊端,多继承弊端就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。而接口中的功能都没有方法体,由子类来明确。
类和类、类和接口、接口和接口之间的关系总结:接口保证了一个类只能继承一个父类 但可以同时实现多个接口!
关于接口的感悟:父类中定义的事物可以理解为最基本的功能,而接口中定义的事物可以理解为扩展的功能。
类与类之间:只能是单继承关系
public class A {
void show(){...}
void showA(){}
}
public class B{
void show(){...}
void showB(){}
}
public class C extends A,B {
//对于C而言 两个show()的执行内容是不一样 所以在调用的时候就会有差异
}
类与接口之间:类可以实现多个接口
public interface A {
void show();
void showA();
}
public interface B{
void show();
void showB();
}
public class DemoC implements A,B {
public void show(){}
public void showA(){}
public void showB(){}
}
接口和接口之间:不存在实现关系,存在多继承关系
public interface A {
void show();
void showA();
}
public interface B{
void show();
void showB();
}
//C继承了两个show()函数,是可以的没有问题,最终当一份使用
public interface C extends A,B {
void show()
void showA()
void showB()
}
接口与抽象类的区别
- 由于方法体的存在,导致某一个类在多继承的时候,如果出现同名函数,则分不清调用顺序
- 由于方法体的不存在,导致一个接口可以多继承接口,就算出现同名函数 也不会导致调用不清,因为具体的实现方式是由接口的实现子类来决定的实现的时候只需要实现一份即可
相同点:
1. 都位于继承的顶端,用于被其他子类实现或继承
2. 都不能实例化
3. 都包含了抽象函数,其子类必须都覆盖这些方法
不同点:
1. 抽象类为部分方法提供实现,避免子类重复实现这些方法,提供代码重用性;接口只能包含抽象方法
2. 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;接口弥补了Java的单继承
二者选择:
1. 优先选用接口,尽量少使用抽象类
2. 如果需要为子类定义其他的有方法体的行为和成员变量的话,只能用抽象类
关于没有抽象函数的抽象类的来源
在开发中,若一个接口中有多个方法,但是实现这个接口只使用其中某些方法,
却仍需要将该接口中其他不使用的方式实现,这样明显不符合我们的需求。
解决:可以使用一个抽象类,作为过度,而这个抽象类实现这个接口,并将其所有方法都以空实现存在。这就是没有抽象方法的抽象类的存在价值。我们只要继承这个抽象类,覆盖其中需要使用的方法即可。
代码实例如下:
//定义一个抽象类作为过渡,而这个抽象类实现这些接口中的所有方法都以空实现存在
public interface inter {
void showA();
void showB();
int showC();
String showD();
}
//AbstractDemoInter此类 只是一个过渡的类 不需要创建对象的
public abstract class AbstractDemoInter implements inter {
//方法体啥也没写 叫做空实现 当然如果有返回值的话 随意返回一个即可
public void showA(){}
public void showB(){}
public int showC(){
return -1;
}
public String showD(){
return null;
}
}
public class Demo extends AbstractDemoInter {
public void showA(){}
public void showB(){}
}



