接口和契约:在这个例子中

时间:2012-01-10 13:18:22

标签: java interface

所以我试图理解接口,但我几乎只看到解释“如何”使用界面的文章,我的问题是理解“为什么”:
所以最好使用 Interface 而不是创建和继承一个类,这可能是没用的,
所以我们在类中实现接口方法,但我不明白为什么这是一件好事,

让我们说:
Car.java这样的类定义了制作汽车的所有代码 我们使用start(),stop()等几种方法创建接口Working.java 我们在Diesel_Car.javaElectric_Car.java等中实施这些方法 那么Car.java的变化是什么?这可能不是最好的例子,因为Car似乎应该是Diesel_Car.java等的父亲 但是在这些类中实现这些方法的意义是什么? Car.java中是否存在以某种方式“调用”Diesel_Car.java类及其接口方法的方法

我已经读过这个界面就像一个“契约”,但我只看到了这个合同的第二部分(实现方法的地方),我在想象第一部分发生在哪里时遇到了一些麻烦? / p>

感谢您的帮助

5 个答案:

答案 0 :(得分:1)

让我们以CarElectric_Car子类为基础的Diesel_Car基类示例,并稍微扩展一下模型。

Car可能具有以下接口

  1. Workingstart()& stop()种方法
  2. Movingmove()turn()& stop()种方法
  3. Car可能包含类AirConditioner的实例,该实例也应该实现接口Working

    Driver对象可以与实现Working的对象进行交互,驱动程序可以start()stop()。 (司机可以单独启动或停止汽车和空调。)

    另外,由于Driver可以自己走动(并且并不总是需要汽车),因此他应该实现接口Moving。 对象Ground现在可以与实现Moving的任何内容进行交互:汽车或司机。

答案 1 :(得分:1)

(非常)人为的例子(为了清楚起见,非通用,删除了错误处理等。)

List theList = new ArrayList();

theListList,在这种情况下由ArrayList实施。假设我们将此传递给第三方API,它在其内容的某处添加了一些内容。

public void frobList(List list) {
    list.add(new Whatever());
}

现在让我们说出于某种原因,我们想要对添加到列表中的项目做一些不寻常的事情。我们无法更改第三方API。但是,我们可以创建一个新的列表类型。

public FrobbableList extends ArrayList {
    public boolean add(E e) {
        super.add(Frobnicator.frob(e));
    }
}

现在在我们的代码中,我们更改了我们实例化的列表并像以前一样调用API:

List theList = new FrobbableList();
frobber.frobList(theList);

如果第三方API采用ArrayList(实际类型)而非List(接口),我们将无法轻松完成此操作。通过不将API锁定到特定实现,它为我们提供了创建自定义行为的机会。

进一步说,这是可扩展,可调试,可测试代码的基本概念。 dependency injection / Inversion of Control之类的东西依赖于编码接口来实现功能。

答案 2 :(得分:1)

我正在尝试将接口的概念解释为契约。

典型的使用方案是,您希望使用java.util.Collections对元素列表进行排序:<T extends java.lang.Comparable<? super T>> void sort(java.util.List<T> ts)

这个签名是什么意思? sort()方法将接受类型为T的java.util.List<T>个对象,其中T是实现可比较接口的对象。

所以,如果您想将Collections.sort()与对象列表一起使用,则需要它们来实现Comparable接口:

public interface Comparable<T>   {   
    int compareTo(T t);
}

因此,如果您实现类型为Car的类并希望使用Collections.sort()按重量比较汽车,则必须在class car中实现Comparable接口/契约。

public class Car implements Comparable<Car> {
   private int weight;

   //..other class implementation stuff

   @Override
   public int compareTo(Car otherCar) {
       if (this.weight == otherCar.weight) return 0;
       else if (this.weight > otherCar.weight) return 1;
       else return -1;
   }
} 
在引擎盖下,Collections.sort()会在对列表进行排序时调用compareTo的实现。

答案 3 :(得分:0)

合同是一个类如何相互协作的概念。这个想法是接口类定义方法返回类型和名称,但不提供如何实现它的想法。这是由实施类完成的。

概念是当接口A定义方法A和B时,实现该接口的任何类必须实现A和B以及它自己的方法。所以它可能会这样工作:

interface InterfaceA {
    void methodA();
    void methodB(String s);
}

public class ClassA implements InterfaceA {
     public void methodA() { 
         System.out.println("MethodA");
     }

     public void methodB(String s) {
         System.out.println(s);
     }
}

合同原则是任何实现接口的东西都必须实现整个接口。任何不这样做的事情都必须是抽象的。

希望这有帮助。

答案 4 :(得分:0)

  

按合同设计(DbC),也称为合同编程和合同设计编程,是一种设计计算机软件的方法。它规定软件设计者应为软件组件定义正式,精确和可验证的接口规范,这些规范扩展了具有前置条件,后置条件和不变量的抽象数据类型的普通定义。根据商业合同的条件和义务的概念隐喻,这些规范被称为&#34;合同&#34;。 Wikipedia

<强>短切。

如果您遵循对接口进行编码的良好做法,您就会知道接口定义了所有实现类必须遵守的合同。

我们设计了Contract Java,它是Java的扩展,其中方法契约在接口中指定。我们确定了三个设计目标。

  • 首先,合同Java程序没有合同和程序 完全满意的合同应该表现得好像没有 Java中的合同。
  • 其次,使用传统Java编译器编译的程序必须能够与程序进行互操作 在Contract Java下编译。
  • 最后,除非一个班级宣布它符合某一特定合同,否则绝不应该因为没有达到这个目标而受到指责 合同。抽象地说,如果调用类型为t的对象的方法m,则只应该为调用者指责 与t和m相关的前置条件合同只应归咎于相关的后置条件合约 与t。 这些设计目标提出了几个有趣的问题,并要求平衡语言设计的决策 软件工程问题。本节介绍了每个主要的设计问题,替代方案,我们的决策, 我们的理由,以及决定的后果。决定不是正交的;一些后来的决定 取决于早先的。

契约中的契约Java是接口中方法签名的装饰。每个方法声明都可能出现 具有前置条件表达和后置条件表达;两个表达式都必须评估为布尔值。该 pre-condition指定调用方法时必须为true的内容。如果失败,则方法调用的上下文是 责备在适当的环境中不使用该方法。后置条件表达式指定了何时必须为真 该方法返回。如果它失败了,那么该方法本身应该归咎于没有建立承诺的条件。 Contract Java不限制合同表达式。不过,良好的编程规则决定了表达式 不应该对计划的结果做出贡献。特别是,表达式不应该有任何副作用。 前置条件表达式和后置条件表达式都是通过方法和伪变量的参数进行参数化的 这个。后者绑定到当前对象。另外,合同的后置条件可以参考 方法的名称,绑定到方法调用的结果。 根据方法调用的类型上下文强制执行合同。如果对象的类型是接口类型,则 方法调用必须满足接口中的所有合同。例如,如果一个对象实现了接口I,则调用 我的方法之一必须检查前提条件和I中指定的后置条件。如果对象的类型是类 类型,该对象没有合同义务。由于程序员总能为任何类创建一个接口,我们 出于效率原因,请保留未选中类类型的对象。 例如,考虑接口RootFloat:

interface RootFloat {
    float getValue ();
    float sqRoot ();
    @pre { this.getValue() >= 0f }
    @post { Math.abs(sqRoot * sqRoot - this.getValue()) < 0.01f }
}

它描述了一个提供sqRoot方法的float包装类的接口。第一个方法getValue没有 合同。它不接受任何参数并返回unwrapped float。 sqRoot方法也不接受任何参数, 但是有合同。前置条件断言未展开的值大于或等于零。结果类型 sqRoot是浮动的。后置条件表明结果的平方必须在浮点值的0.01之内。 即使合同语言足够强大,在某些情况下也可以指定完整的行为,例如 在前面的例子中,总体甚至部分正确性不是我们设计这些合同的目标。通常,合同 无法表达方法的完整行为。事实上,在透露的信息量之间存在着紧张关系 合同可以满足的界面和验证量。 例如,请考虑此堆栈接口:

interface Stack {
    void push (int i);
    int pop ();
}

由于界面中只有推送和弹出操作,因此无法在推送后指定顶部 堆栈中的元素是刚刚推送的元素。但是,如果我们使用top操作扩充接口 揭示堆栈中最顶层的项目(不删除它),然后我们可以指定push将项目添加到顶部 堆栈:

interface Stack {
     void push (int x);
     @post { x = this.top() }
     int pop ();
     int top ();
}

总之,我们不限制合同的语言。这使合同语言尽可能灵活; 合同表达式评估甚至可能有助于计算的最终结果。尽管有灵活性 合同语言,并非所有理想的合同都是可表达的。有些合同无法表达,因为它们可能 涉及检查不可判断的属性,而其他人则无法表达,因为界面不允许 观察结果。