java-接口回调

原创:http://write.blog.csdn.net/mdeditor

接口的定义:

接口个人理解为是一个约束。用接口来标识一种没有任何行为的抽象类型。
java语言不支持一个类有多个直接的父类(多继承),但可以实现(implements)多个接口,间接的实现了多继承。
继承是亲爹,只有一个。
接口是干爹,可以有多个。

接口中的属性:

1、接口中的成员变量默认都是public,static,final类型的(都可省略),必须被显示初始化,即接口中的成员变量为常量(大写,单词之间用”_”分隔),接口中只能包含public,static,final类型的成员变量和public,abstract类型的成员方法。

  public interface A
   {
    int var; //错,var是常量,必须显示初始化 
    void method(){...};   //错,接口中只能包含抽象方法
    protected void method2(); //错,接口中的方法必须是public类型
    static void method3(){...};   //错,接口中不能包含静态方法
   }

2、接口中的方法默认都是public,abstract类型的(都可省略),没有方法体,不能被实例化。

 public interface A
   {
    int CONST = 1; //合法,CONST默认为public,static,final类型
    void method(); //合法,method()默认为public,abstract类型
    public abstract void method2(); //method2()显示声明为public,abstract类型
   }

3、接口中的类都是抽象的,接口中没有构造方法,不能被实例化。用abstract定义的类为抽象类。

 public interface A
   {
    public A(){...}; //错,接口中不能包含构造方法
    void method();
    public abstract class Ship {};
   }

4、接口必须通过类来实现它的抽象方法

   public class A implements B{...}

5、当类实现了某个接口时,它必须实现接口中的所有抽象方法,否则这个类必须声明为抽象的

通过抽象类来控制接口中属性常量:

创建一个接口,在该接口中有一个方法和一个属性,要求这个属性能够随着市场的变化而改变,但是对于所有实现该接口的类而言,属性的值应该保持一致。
接口中的属性肯定是常量了即public static final 类型 变量 = 值;既然是final就不能二次赋值。
不过可以用一个抽象类B继承此接口A(并去除接口A中的属性常量),在抽象类B中扩充此接口A,扩充一个属性(它不是final的)和一个abstract 方法。

//第一个具体类C继承抽象类B
public class C extends B{
    public static void main(String[] args) {
        C c = new C();
}

//第二个具体类D继承抽象类B
class D extends B{

}
//接口A
interface A{
    void method1();
}
//抽象类B实现接口A,虽然里面没抽象方法但为以后迭代做铺垫
abstract class B implements A{
    public void method1(){};
    public void setter(int i){
        this.i = i;
    }
    public static int i = 1;
}
/*
 * 程序输出:
 *  C The orgin : 1
    C The alter : 9
    D The orgin : 9
    D The alter : 99
    C The after : 99
    D The after : 99
 */

接口回调

接口回调是指:可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。实际上,当接口变量调用被类实现的接口中的方法时,就是通知相应的对象调用接口的方法,这一过程称为对象功能的接口回调。

回调案例1:

interface People{
void peopleList();
}
class Student implements People{
public void peopleList(){
System.out.println("I’m a student.");
}
}
class Teacher implements People{
public void peopleList(){
System.out.println("I’m a teacher.");
}
}
public class Example{
public static void main(String args[]){
People a; //声明接口变量
a=new Student(); //实例化,接口变量中存放对象的引用
a.peopleList(); //接口回调
a=new Teacher(); //实例化,接口变量中存放对象的引用
a.peopleList(); //接口回调
}
}
结果:
I’m a student.
I’m a teacher.

上溯造型(向上转型):

一个对象可以作为它自己的类型使用,或者作为它的基础类型(父)的一个对象使用。取得一个对象句柄,并将其作为基础(父)类型句柄使用就叫做“上溯造型”。

注意:每个对象都是父类的对象,反之则不然!

注意:如果在子类中定义了一个与超类相同的方法,那么子类中的这个方法就覆盖了超类中相同的这个方法。

package com.he.demo;

class Note{
    private int value;
    private Note(int val){
        value=val;
    }
    public static final Note middleC=new Note(0);
    public static final Note cSharp=new Note(1);
    public static final Note cFlat=new Note(2);
}


class Instrument{
    public void play(Note n){
        System.out.println("Instrument.play");
    }
}

class Wind extends Instrument{
    public void play(Note n) {
        System.out.println("Wind.play");
    }
}


public class Music {
    //静态方法,接受Instrument句柄。
    public static void tune(Instrument i){
        i.play(Note.middleC);
    }

    public static void main(String[] args) {
        //调用静态方法,传入Instrument的子类。
        Wind wind=new Wind();
        tune(wind);
    }

}

为什么要上溯造型(向上转型)?

  • 上面的程序可能看起来有点奇怪,为什么应该有意忘记一个对象类型呢?进行上溯造型时,就可能产生这方面的疑惑。
public class Music {
    //静态方法,接受Instrument句柄。
    public static void tune(Instrument i){
        i.play(Note.middleC);
    }

    public static void main(String[] args) {
        //调用静态方法,传入Instrument的子类。
        Wind wind=new Wind();
        tune(wind);
    }

}

public class Music {
   //静态方法,接受Wind 句柄。
    public static void tune(Wind i){
        i.play(Note.middleC);
    }
    //调用静态方法,传入Wind 句柄。
    public static void main(String[] args) {
        Wind wind=new Wind();
        tune(wind);
    }

}
  • 为什么不写成这样?让tune()简单取得一个Wind句柄,将其作为自己的变量使用,这样写似乎更加简单直观?注意:假如那样做,就需要为系统内Instrument的每一张类型都写一个全新的tune()。假如加上Stringed(弦乐)和Brass(铜管)这两种乐器。看下方代码:
package com.he.demo;

class Note{
    private int value;
    private Note(int val){
        value=val;
    }
    public static final Note middleC=new Note(0);
    public static final Note cSharp=new Note(1);
    public static final Note cFlat=new Note(2);
}


class Instrument{
    public void play(Note n){
        System.out.println("Instrument.play");
    }
}

class Wind extends Instrument{
    public void play(Note n) {
        System.out.println("Wind.play");
    }
}


class Stringed extends Instrument{
    public void play(Note n) {
        System.out.println("Stringed.play");
    }
}


class Brass extends Instrument{
    public void play(Note n) {
        System.out.println("Brass.play");
    }
}


public class Music {

    public static void tune(Wind i){
        i.play(Note.middleC);
    }

    public static void tune(Stringed i){
        i.play(Note.middleC);
    }
    public static void tune(Brass i){
        i.play(Note.middleC);
    }
    public static void main(String[] args) {
        Wind wind=new Wind();

        Stringed stringed=new Stringed();

        Brass brass=new Brass();

        tune(wind);
        tune(stringed);
        tune(brass);
    }

}

上方这种写法存在一个极大的弊端,必须为每个Instrument类编写一个与类紧密相关的方法,这意味着第一次要求多的变成两。以后想要添加一个新类型。仍然需要大量的编码工作。所以使用上溯造型就会很方便。

后期绑定(动态绑定):

  • 后期绑定:它意味着绑定在运行期间进行,以对象的类型为基础。后期绑定也叫“动态绑定”。
  • java中绑定的所有方法都是后期绑定技术,除非声明成final,这意味着不必决定是否进行后期绑定。它是自动发生的。
  • 我们可以将一条消息发给一个对象,让对象自行判断要做什么事情。
  • 子类重写父类中的方法,调用子类中的方法。
  • 子类没有重写父类中的方法,所以到父类中寻找相应的方法。
  • 动态绑定只是针对对象的方法,对于属性无效。因为属性不能被重写。
package com.he.demo;

class Instrument3{
    public void play(){
        System.out.println("Instrument3.play()");
    }
    public String what(){
        return "Instrument3";
    }
    public void adjust(){

    }
}

class Wind3 extends Instrument3{
    public void play(){
        System.out.println("Wind3.play()");
    }
    public String what(){
        return "Wind3";
    }
    public void adjust(){

    }
}


class Percussion3 extends Instrument3{
    public void play(){
        System.out.println("Percussion3.play()");
    }
    public String what(){
        return "Percussion3";
    }
    public void adjust(){

    }

}

class Stringed3 extends Instrument3{
    public void play(){
        System.out.println("Stringed3.play()");
    }
    public String what(){
        return "Stringed3";
    }
    public void adjust(){

    }
}


class Brass3 extends Wind3{
    public void play() {
        System.out.println("Brass3.play()");
    }
    public void adjust() {
        System.out.println("Brass3.adjust()");
    }
}

class Woodwind3 extends Wind3{
    public void play() {
        System.out.println("Woodwind3.play()");
    }
    public void adjust() {
        System.out.println("Woodwind3.adjust()");
    }

}


public class Music3 {
    static void tune(Instrument3 i){
        i.play();
    }

    static void tuenAll(Instrument3[] e){
        for(int i=0;i<e.length;i++){
            tune(e[i]);
        }
    }

    public static void main(String[] args) {
        Instrument3[] instrument3=new Instrument3[5];
        int i=0;
        instrument3[i++]=new Wind3();
        instrument3[i++]=new Percussion3();
        instrument3[i++]=new Stringed3();
        instrument3[i++]=new Brass3();
        instrument3[i++]=new Woodwind3();
        tuenAll(instrument3);   
    }
}

向上转型与接口回调的区别:

看似向上转型和接口回调是一回事。看下面两句话,均出自Thinking in Java。
使用接口的核心原因:为了能够向上转型为多个基类型[1]。即利用接口的多实现,可向上转型为多个接口基类型(具体见《抽象与接口》章节6)。
从实现了某接口的对象,得到对此接口的引用,与向上转型为这个对象的基类,实质上效果是一样的。(此句摘自Thinking in Java 3rd 接口与内部类一章)
所以,我认为,这两个概念是从两个方面来解释一个行为。接口回调的概念,强调使用接口来实现回调对象方法使用权的功能(下一章节详细分析)。而向上转型则牵涉到多态和运行期绑定的范畴。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值