如何理解 Java 中的接口(interface)()
【如何理解 Java 中的接口(interface)()】Java 中的接口(interface)如何理解?
interface 的实现(implement)不同于类(class)的继承(extend),因为 interface 中不包含方法(method)的实现(implement)。
于是很多人就会疑惑,既然 interface 中的 method 没有 implement,那么 extend(implement)它做什么呢?
这些人实际上把 interface 的 implement 和 class 的 extend 混淆了——class 的 extend 中,子类可以从父类中取得一些实现(implement),从而实现(realize)代码复用。
但是 interface 恰恰不是用来 realize 代码复用的。
为了表现 interface 的作用,我们来看一个例子。
这是一个 class (修饰符已略去)
class Tiger
{
void run()
{
// method body
}
void eat()
{
// method body
}
}
可以看到有 2 个方法。
Java 是一门静态类型语言,我们可以用 class 名作为参数类型,这是众所周知的。
void feed(Tiger tiger)
{
tiger.eat()
}
但是,如果我们有了新的需求呢?
例如,我们又有了一个新的 class
class Fish
{
void swim()
{
// method body
}
void eat()
{
// method body
}
}
这个
Fish
class 和Tiger
class 一样有一个eat()
method,我们希望feed
method 也能对Fish
对象起作用。当然,我们可以使用 method 的重载(overload)。
void feed(Tiger tiger)
{
tiger.eat()
}
void feed(Fish fish)
{
fish.eat()
}
不过可以遇见,要是这样的 class 越来越多,我们的工作量就很大了。更何况,这里的代码基本上完全重复了嘛。
这说明什么?说明 overload 的方案的扩展性并不强,我们需要一个一劳永逸的方案。
理论上,我们可以采用这么一个方案
维护一个集合(set)
FeedableAnimals
,用来记录那些我们希望feed
method 能起作用的 class。然后我们的feed
method 就这么写。(Java 不支持这种语法,因此你只能在脑中这么做)void feed(FeedableAnimials animal)
{
animal.eat()
}
然后把
Tiger
、Fish
等等放进 FeedableAnimals
里。现在,我就明确告诉你,这个
FeedableAnimals
就是 interface 概念的雏形。现在,我们离真正的 interface 只差几步了。
考虑我们现在这个方案,看起来比原来好多了。
但是仔细想想,还有一些不足
第一,
FeedableAnimals
作为一个 set,是需要维护的:如果我们添加新的一种 class,比如Bear
,那么我们必须修改其定义,将这个 classBear
添加进去。显然,这不符合开闭原则——对修改封闭。
第二,假设我们把
Bear
添加进来,但是由于疏忽,我们没有确认Bear
是否有一个eat()
method。不妨假设,Bear
实际上长这样class Bear
{
void run()
{
// method body
}
void eat(Fish fish)
{
// method body
}
}
这时
Bear
有一个methodeat(Fish)
,于是我们的工作人员产生了疏忽,误以为Bear
有一个eat()
method(这两者的方法签名(Method Signature)并不相同!)就把它加了进来。固然,我们可以人工一个个浏览
FeedableAnimals
中的 class,检查其是否具有正确的 method signature。但是如果能让编译器来做这件事显然更合理,因为保证语法没有错误是编译器的工作。于是,在 Java 中,真正的 interface 是这样设计的:
- interface 并不记录 class,相反,是 class 记录 interface。
- interface 可以定义抽象 method,抽象 method 仅仅包含 method signature。如果 class implement 了 interface,那么编译器会检查 class 中是否有对应的 method signature。
定义一个 interface
FeedableAnimals
interface FeedableAnimals
{
void eat();
}
然后
feed
method 应该这么写:void feed(FeedableAnimals animal)
{
animal.eat()
}
这时,由于 interface
FeedableAnimals
中确实规定了 method signatureeat()
,因此编译器能够保证对于任何一个 implement 了 FeedableAnimals 的对象 animal 来说,animal.eat()
的调用不会出现语法错误(例如animal 找不到 eat() 方法
这样的报错)。这样,从编译器层面就排除了feed
method 跑不通的可能性。Bear
class 应该这么写class Bear implements FeedableAnimals
{
void eat()
{
// method implement
}
}
这样一旦出现诸如 method signature 不符、或者是 method 忘了实现这样的错误,由于我们已经声明了
Bear
是 implement 了 interface FeedableAnimals
的,因此编译器就能帮我们检查和发现这些错误,从而在编译阶段就杜绝这些错误的可能性。并且以后再出现新的 class,我们也只需要像
Bear
那样写,就能保证feed
method 能自动被应用在这些新 class 的对象上。推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 深入理解Go之generate
- 考研英语阅读终极解决方案——阅读理解如何巧拿高分
- 由浅入深理解AOP
- 如何寻找情感问答App的分析切入点
- mybatisplus如何在xml的连表查询中使用queryWrapper
- MybatisPlus使用queryWrapper如何实现复杂查询
- 事件代理
- 逻辑回归的理解与python示例
- Java|Java OpenCV图像处理之SIFT角点检测详解