接口允许您在运行时提供不同的实现,注入依赖项,分离关注点,并用于测试不同的实现。
只需将接口视为类保证实现的合同。实现接口的具体类是无关紧要的。不知道这是否有帮助。
认为一些代码可能会有所帮助。这并不能解释所有关于接口的内容,请继续阅读,但我希望这可以让您开始了解。重点是您可以改变实现方式...
package stack.overflow.example;
public interface IExampleService {
void callExpensiveService();
}
public class TestService implements IExampleService {
@Override
public void callExpensiveService() {
// This is a mock service, we run this as many
// times as we like for free to test our software
}
}
public class ExpensiveService implements IExampleService {
@Override
public void callExpensiveService() {
// This performs some really expensive service,
// Ideally this will only happen in the field
// We'd rather test as much of our software for
// free if possible.
}
}
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
// In a test program I might write
IExampleService testService = new TestService();
testService.callExpensiveService();
// Alternatively, in a real program I might write
IExampleService testService = new ExpensiveService();
testService.callExpensiveService();
// The difference above, is that we can vary the concrete
// class which is instantiated. In reality we can use a
// service locator, or use dependency injection to determine
// at runtime which class to implement.
// So in the above example my testing can be done for free, but
// real world users would still be charged. Point is that the interface
// provides a contract that we know will always be fulfilled, regardless
// of the implementation.
}
}
IExampleService
)中声明的,在这种情况下,必须/包括接口中声明的方法,对吗?如果是这样,那么接口的使用就是“汇集”所有具有共同方法的类,对吗?此外,接口变量可以“包含”每个对象还是只能包含实现它的对象? - Asaf接口可以用于许多事情。最常见的用途是多态和依赖注入,在这里您可以在运行时更改依赖关系。例如,假设您有一个名为Database的接口,其中有一个名为getField(...)
的方法。
public interface Database
{
public void getField(String field);
}
现在假设您在应用程序中使用两个数据库,具体是MySQL和PostgreSQL,取决于您的客户端。您将拥有两个具体类:
现在假设您在应用程序中使用两个数据库,具体是MySQL和PostgreSQL,取决于您的客户端。您将拥有两个具体类:
public class MySQL implements Database
{
// mysql connection specific code
@Override
public void getField(String field)
{
// retrieve value from MySQL
}
}
并且...
public class PostgreSQL implements Database
{
// postgre connection specific code
@Override
public String getField(String field)
{
// retrieve value from Postgre
}
}
现在根据您客户端的偏好,您可以在主要文件中实例化一个MySQL或PostgreSQL类。
public static void main(String args[])
{
if (args[2].equals("mysql"))
Database db = new MySQL();
else
Database db = new PostgreSQL();
}
//now get the field
String foo = db.getField();
或者您可以使用 Factory/Abstract Factory 或 JS 或 Ruby 脚本引擎。
if (args[2].equals("mysql")) MySQL db = new MySQL(); else PostgreSQL db = new PostgreSQL();
这种写法的意图不够明确。 - user1451111它们用于实现多态性,而无需使用继承。
public void passMeSomethingThatICanClose(AutoCloseable bar) {
//stuff goes here
bar.close(); //the compiler knows this call is possible
}
public class BrokenCloseable implements AutoCloseable {
public void colse() { //ERROR — see the typo?
//blah
}
}
这是一个错误,因为方法名称不正确。在Java中,编译器可以(并且会)告诉你这一点。但是,如果你只负责实现方法本身,你将不会收到错误信息。相反,你的类将会静默地“不是”可自动关闭的。其他一些语言(Python是一个很好的例子)不会像Java那样处理 —— 相反,它们会按照你在问题中建议的方式处理。这被称为“鸭子类型”(如果它看起来像鸭子,叫起来像鸭子,那就把它当作鸭子对待),这也是一种可行的方法,只是与Java不同。
接口规定了功能。从某种意义上说,它们提供了多继承。假设我有一个函数,用于执行列表操作。但并不仅限于列表,可以是任何集合类型。只要可以遍历其中的内容,就可以使用 Iterable 接口。
public int randomMethod(Iterable<Integer> li) {
...
}
现在,这个功能可以与列表和哈希集合等完全不同的实现方式并属于不同的类结构一起使用,但是所有这些都提供了基本功能。
这与一个大型游戏循环不同,它会更新所有扩展公共类的实体,尽管可以使用接口。
List<T>
接口来说,你只需要在一个地方决定是否要在特定场合使用ArrayList<T>
或者LinkedList<T>
(或者其他相关的类)。但是它们都实现了List<T>
接口。因此,无论它们如何工作,甚至是否在层次结构上相关,它们都保证公开一组方法供你使用,你不必在代码的每个点上都知道你最终得到的集合对象背后的实现。你的问题太过宽泛,无法在单个帖子中回答而不会变得过于牵强或过于普遍而没有用处。因此,我将尝试给出一个有意义的例子,说明接口何时非常有用。
假设你有许多类别代表各种类型的对象。也许有十几个,或者任何足够大的数量。你有另一个类中的一些函数,希望能够对任意对象进行排序。为此,它需要能够比较任意两个对象,并确定一个是否比另一个更大或更小。由于每个对象都可以是你许多类型的类之一,因此这个比较函数将非常繁琐,因为它必须确定每个对象的类型,然后通过调用每个类的适当逻辑来进行比较。
这时就需要用到接口。此时,你可以让所有的类都实现一个接口,比如Comparable,该接口指定任何实现它的类都将实现一个Compare方法。
现在,你的排序对象函数编写起来将变得非常简单,因为它只需依赖于任何需要比较的对象都具有相同的Comapre方法即可。
这只是一个例子,而且是一个相当常见的例子。
someInterface a = new someClass();
? 我部分理解它的作用,但如果函数在类本身中已经实现,为什么还需要它呢?顺便说一下,感谢您的回答,我最好理解它了! :) - Asaf