2012-01-10 36 views
1

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

比方说, :
类似Car.java定义了所有的代码,使汽车
我们创建几种方法如启动界面Working.java(),停止()等
我们实施的方法Diesel_Car.javaElectric_Car.java
那么什么改变Car.java?这可能不是最好的例子,因为看起来Car应该是Diesel_Car.java等的母公司,
但是在这些类中实现这些方法有什么意义?
有没有方法Car.java,以某种方式“调用”Diesel_Car.java类和它的接口方法?

我读过界面就像是一个“契约”,但我只看到了这个契约的第二部分(这个方法实现的地方),我有一些麻烦想象第一部分发生在哪里?

感谢您的帮助

+0

[Class vs. Interface]的可能重复(http://stackoverflow.com/questions/2271104/class-vs-interface) – 2012-01-10 13:23:56

+0

“第一部分”是合同的*定义*,第二部分是实施。这是一件“好事”,因为它意味着功能与实现无关(我可以将每个实现视为Car),并且类可以实现多个接口。 – 2012-01-10 13:24:45

+0

此问题之前已被问过,请参阅http://stackoverflow.com/questions/416331/java-interfaces – Peter 2012-01-10 13:24:49

回答

1

让我们把你的基类的Car的例子有Electric_CarDiesel_Car子类,并扩展模型位。

车可能具有以下接口

  1. Working:与start() & stop()方法
  2. Moving:与move()turn() & stop()方法

Car可能包含类的一个实例这也应该实现接口Working

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

另外,由于Driver可以自己走动(并且并不总是需要一辆车),所以他应该实现接口Moving。 对象Ground现在可以与任何实现Moving的任何事物交互:无论是汽车还是司机。

+0

感谢Nitzan,我想我已经开始更多地理解它了,但我仍然有点失落。你会有另一个例子来描述接口吗?我真的很感激 – Paul 2012-01-10 14:07:23

+0

我会试着想想另一个例子, 仍然试图明白你到底有什么困难。在上面的例子中,你仍然缺少什么? – 2012-01-10 18:52:39

+0

我想知道在什么情况下地面需要处理一辆车,然后改变汽车的类型为司机。这更多的是使用界面的“为什么”。 – Paul 2012-01-10 18:59:48

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); 
    } 
} 

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

希望这会有所帮助。

+0

感谢MikeyB,这是我们应该做什么来使用界面,但不是如何工作,接下来当方法在类中实现时会发生什么...... – Paul 2012-01-10 13:45:29

+0

@Paul你是什么意思,“接下来会发生什么“?你问的是如何Java方法调度工作? – 2012-01-10 18:30:34

+0

@Dave Newton:好吧,看起来主要的一点是能够改变我们正在处理的对象的类型(通过将接口指定为对象的类型),但为什么要这样做呢? – Paul 2012-01-10 19:05:02

0

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

捷径。

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

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

  • 首先,合同Java程序没有与 完全满足合同合同和方案应该表现为,如果他们不需要在Java 合约运行。其次,使用传统Java编译器编译的程序必须能够与在Contract Java下编译的程序 进行互操作。
  • 最后,除非一个班级宣称它符合特定的合同,否则不应该因为未能符合该合同而被指责。抽象地说,如果调用类型为t的对象的方法m,则仅应将责备者指定为与t关联的前置条件合同,并且应该仅针对与t关联的后置条件合同 来指责m。 这些设计目标引发了一些有趣的问题,并要求决策权衡软件工程问题的语言设计。本节介绍每个主要设计问题,备选方案,我们的决定,我们的理由以及决策的影响。决定不是正交的;一些后来的决定 依赖于以前的决定。

契约中的契约Java是接口中方法契约的装饰。每个方法声明可能带有前置条件表达式和后置条件表达式 ;这两个表达式都必须评估为布尔值。前提条件指定调用该方法时必须满足的条件。如果失败,该方法调用的上下文将被指定为 ,因为没有在适当的上下文中使用该方法。后置条件表达式指定该方法返回时必须成立的条件。如果它失败了,那么这个方法本身就成了没有建立承诺条件的原因。 合约Java不限制合约表达式。尽管如此,良好的编程规范仍然规定表达式 不应该对程序的结果有所贡献。特别是,表达式不应该有任何副作用。 前后条件表达式都是通过方法的参数和伪变量 来参数化的。后者与当前对象绑定。此外,合同的后续条件可能涉及方法的名称,该方法与方法调用的结果绑定。 基于方法调用的类型上下文强制执行合同。如果对象的类型是接口类型,那么方法调用必须满足接口中的所有合同。例如,如果一个对象实现接口I,则对我方法之一的呼叫 必须检查在I中指定的先决条件和后置条件。如果对象的类型是类别 类型,则该对象没有合同义务。由于程序员总是可以为任何类创建一个接口,所以我们 为了效率的原因不选择类类型的对象。 举一个例子,考虑接口RootFloat:

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

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

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

在界面中仅push和pop操作,这是不可能指定,一推之后,在堆栈顶部 元素是元素刚被推。但是,如果我们有一个顶级的操作, 显示堆栈上的最上面的项目(不将其移除)增强的界面,那么我们可以指定推增加了项目到 堆栈的顶部:

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

在总结,我们不限制合同的语言。这使合同语言尽可能灵活;合同表达评估甚至可能有助于计算的最终结果。尽管合同语言具有灵活性,但并非所有可取的合同都是可以表达的。一些合同是不可压缩的,因为它们可能涉及检查不可判定的属性,而另一些合同是不可压缩的,因为界面不允许有足够的观察结果。

+0

虽然这是Java本身的很远的地方。 – 2012-01-10 14:25:37

1

(很)人为的例子(为了清楚起见,非泛型,删除了错误处理等)。

List theList = new ArrayList(); 

theListList,在这种情况下,由一个ArrayList实现。比方说,我们将其传递给第三方API,这个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这样的东西依靠编码来实现接口功能。

+0

感谢戴夫,我想我终于明白你的例子了,谢谢你的回答;-) – Paul 2012-01-11 14:06:38

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是实现接口Comparable的对象。

所以,如果你想与你的对象列表使用Collections.sort(),你会需要它们来实现Comparable接口:

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

所以,如果你实现一个类Car类型并且想要通过使用Collections.sort()来比较汽车的重量,您必须在班级汽车中实施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()会在您对sortTo排序列表时调用您的实现。

+0

没问题,所以这个方法就像一个回调,处理这个回调的代码已经写在其他地方了...好吧,我想我多了解一点,非常感谢你的回答! – Paul 2012-01-11 14:10:49