我们为什么需要 ‘接口’ ?
接口( interface ) 在 Java 中是一个很有意思的存在,它本身并不实现特定功能,只是定义一些常量和标记某些抽象方法。在很长时间里,个人一直困惑接口在 Java 中到底有何实际意义。直到遇见了Thread 和 Runnable …….
一个线程的例子
关于使用线程,比较传统的方法是通过继承 Thread 类,比如 Demo.java 文件如下:
class B extends Thread{
public void run(){
for(int i = 0; i < 5; i ++){
System.out.println("In Thread B!");
try{Thread.sleep(500);
}catch(Exception e){}
}
}
}
class C extends Thread{
public void run(){
for(int i = 0; i < 5; i++){
System.out.println("In Thread C!");
try{Thread.sleep(500);
}catch(Exception e){}
}
}
}
public class Demo{
public static void main(String[] args){
Thread b = new B();
Thread c = new C();
b.start();
c.start();
}
}
在我的系统上输出结果为:
In Thread B!
In Thread C!
In Thread C!
In Thread B!
In Thread C!
In Thread B!
In Thread C!
In Thread B!
In Thread C!
In Thread B!
但是如果需要使用线程的类已经继承了其他类呢?
众所周知, Java 中的类只能唯一的继承。因此,在比如上面的例子中。如果 B, C 类继承自另一个类 A 的话,就不可能再继承 Thread 类了:
class A {
public abstract void run();
/* 虽然这里有一个 run() 函数,但是和线程没有任何关系。*/
}
class B extends A {
public void run(){
for(int i = 0; i < 5; i++){
System.out.println("In Thread B!");
try{
Thread.sleep(500);
}catch(Exception e){}
}
}
// ...
在这种情况下,就需要用到接口了, Java 中虽然类只能唯一的继承,但是接口却可以有多个实现,运用线程的另一种方式就是实现 Runnable 接口:
class A {
public abstract void run();
}
class B extends A implements Runnable{
public void run(){
/* 因为 A 类的 run() 方法参数和返回类型和 Runnable 需要实现的一样,
* 所以编译器并不能区分,执行时会被当作Runnable 中的 run() 方法。
* */
for(int i = 0; i < 5; i++){
System.out.println("In Thread B!");
try{Thread.sleep(500);
}catch(Exception e){}
}
}
class C extends A implements Runnable{
public void run(){
for(int i = 0; i < 5; i++){
System.out.println("In Thread C!");
try{Thread.sleep(500);
}catch(Exception e){}
}
}
public class Demo{
public static void main(String[] args){
Runnable b = new B(); // 注意这里的 b 可以引用为 Runnable 对象.
C c = new C();
Thread tb = new Thread(b);
Thread tc = new Thread(c);
tb.start();
tc.start();
}
}
通过上面的例子可以发现,如果是直接继承自 Thread 的类,那么在新建一个 Thread 对象时不需要传入参数。但是在有些情况下并不能允许继承 Thread 类,就需要用到线程的那个类实现 Runnable 的接口,实现 Runable 接口的类可以作为一个 Runnable 对象传入 Thread ,然后执行线程的操作。 上面的 A 类虽然有符合要求的 run() 方法。但是如果,比如 C 类并没有使用 Runnable 接口,编译器会出现类似下面的错误:
Thread tc = new Thread(c);
^
constructor Thread.Thread(Runnable) is not applicable
(argument mismatch; C cannot be converted to Runnable)
因为编译器没有理由检查函数中是否有符合 Thread 条件的 run() 方法,但是如果一个类应用了 Runnable 接口。它就一定会有符合要求的 run() 方法。
总结一下:接口在 Java 中可以将实现了某一接口的类,转化成关于这个接口的对象,然后将这个对象传入到需要某一相应接口的实例化对象作为参数的函数中。
一定程度上可以将接口看成一张质量认证书,一个商品( 这里是类 )需要获取相关质量认证,就必须满足相应条件( 具体来说就是按要求实现特定方法 )。当带着这件商品出入境( 传入 Thread )时,只要能拿出质量认证( 实现 Runnable 接口 ),海关就会放行( 进行下一步编译 ),否则海关就不允许通过( 编译报错 )。
可以使用匿名类,快速实现一个线程:
Thread t = new Thread(new Runnable{
public void run(){
// Do something ...
}
});
t.start();