我刚刚开始学习Java
我遇到了Interfaces
,我知道该如何使用,但仍然无法完全掌握它的想法
据我了解,interfaces
通常由类实现,然后必须实现接口中声明的方法
问题是 - 究竟是什么意思?从接口实现方法作为普通类方法不是更容易吗?使用接口的优势究竟是什么?
我试过在谷歌上寻找答案。有很多,但我仍然无法理解它的意义。我还阅读了this question及其答案,但整个合同事情让它变得更加复杂......
希望有人可以简化它! :)
提前谢谢!
答案 0 :(得分:11)
接口允许您在运行时提供不同的实现,注入依赖关系,单独关注,使用不同的实现进行测试。
只需将接口视为类保证实现的合同即可。实现接口的具体类是无关紧要的。不知道这是否有帮助。
想一些代码可能有所帮助。这并不能解释接口上的所有内容,继续阅读,但我希望这能让你开始。这里要点是你可以改变实施......
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.
}
}
答案 1 :(得分:8)
接口可用于许多事情。最常用的是多态和dependency injection,您可以在运行时更改依赖关系。例如,假设您有一个名为Database的接口,它有一个名为getField(...)
的方法。
public interface Database
{
public void getField(String field);
}
现在假设您在应用程序中使用了两个数据库,具体取决于您的客户端: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();
或者您可以使用JS或Ruby使用Factory / Abstract Factory或脚本引擎。
答案 2 :(得分:4)
他们要实现polymorphism而不需要使用继承。
答案 3 :(得分:3)
接口的想法是指定一个类可以实现的契约。你读过的另一篇文章大致涵盖了这个想法。
直接从接口直接实现方法的原因是,其他方法可以使用接口类型来要求存在这些方法。
我们以AutoCloseable为例。唯一的方法是close方法,但是方法使用类型表达“我需要一个可以关闭的参数”的思想要比列出所有方法容易得多:
public void passMeSomethingThatICanClose(AutoCloseable bar) {
//stuff goes here
bar.close(); //the compiler knows this call is possible
}
如果我们没有简洁的类型名称,那么该方法必须明确列出要求,这不是很容易,特别是当接口“contract”有很多方法时。
这也是另一种方式。通过使用特定关键字“实现AutoCloseable”来表示意图,编译器可以告诉您是否未正确实现合同。
public class BrokenCloseable implements AutoCloseable {
public void colse() { //ERROR — see the typo?
//blah
}
}
这是一个错误,因为该方法名称错误。在Java中,编译器可以(并将会)告诉您这一点。但是,如果您自己负责实施这些方法,则不会出现错误。相反,你的班级会默默地“不”自动关闭。
其他一些语言(Python就是一个很好的例子)不要这样做 - 而是按照你在问题中建议的方式做事。这被称为“鸭子打字”(如果它看起来像一只鸭子,像鸭子一样呱呱叫,像鸭子一样对待它),它也是一种可行的做事方式,与Java不同。
答案 4 :(得分:2)
接口指定功能。在某种程度上,它们提供了多重继承。假设我有一个用列表做某事的函数。但不仅仅是一个列表,只是任何集合。我可以循环内容的任何东西。所以我使用了Iterable接口。
public int randomMethod(Iterable<Integer> li) {
...
}
现在,这适用于Lists和HashSets以及实现的所有不同的东西,它们以完全不同的方式工作,属于不同的类结构,但都提供了这个基本功能。这与一个大型游戏循环不同,.update()
所有扩展公共类的实体,尽管可以使用接口。
答案 5 :(得分:2)
例如,使用List<T>
界面。您只能在一个点决定是否要在特定场合使用ArrayList<T>
或LinkedList<T>
(或其他相关内容)。
然而他们都实现了List<T>
接口。因此,无论它们如何工作或者它们是否甚至是分层相关的,都可以保证它们公开了一组您可以使用的方法,并且您不必在代码中的每个点知道集合对象背后的实现。结束了。
答案 6 :(得分:2)
因为“死亡的致命钻石”。 让我们考虑一下这种情况:
您在允许多继承的编程语言中编写类(名称:Parent
)。该类包含一个实例变量(int a;
)和一个方法(void hello()
)。然后创建两个类(Kid1
和Kid2
)。每个类都延伸Parent class
。然后,您在hello();
和Kid1
类中覆盖Kid2
方法,并为a
变量指定一些值,Kid1
和Kid2
也是如此类。在最后一步中,您将创建扩展Main
和Kid1
类(多重继承)的第4个类(如Kid2
)。现在......问题是,hello();
方法将Main
类继承哪个,以及a
变量的值。
由于Java中没有多重继承,我们有interfaces
...包含抽象方法的抽象类。我们可以实现多个接口,但在条件下我们必须覆盖实现interface
包含的所有方法。
答案 7 :(得分:1)
我建议你看一下Strategy Pattern和作文模式。
答案 8 :(得分:1)
你的问题可以在一篇文章中广泛回答,而不是过于拙劣或过于通用而没有用处。因此,我将尝试给出一个有希望的有用的例子,说明接口何时非常有用。
假设您有许多代表各种类型对象的类。可能是十几个,或任何足够大的数字。并且你在另一个类中有一些函数,它希望能够对任意对象进行排序。为此,它需要能够比较任何两个对象并确定一个是否大于或小于另一个。由于每个对象可以是您的多种类中的任何一种,因此这种比较函数编写起来非常繁琐,因为它必须确定每个对象的类型,然后通过为每个类调用适当的逻辑来进行比较。 p>
输入界面。此时,您可以让所有类都实现一个接口,例如Comparable,它指定实现它的任何类都将实现Compare方法。 现在你的命令对象的函数变得很简单,因为它只能依赖于它需要比较的任何类对象具有相同的Comapre方法这一事实。 这只是一个例子 - 而且是一个相当普遍的例子。