Java 中, new 对象时,用接口作为接收变量到底有什么好处呢?

2019-08-16 14:03:46 +08:00
 Wangjl

这个问题百度了很多一直也没看明白,百度上各种文章写的也不太清晰。

有没有通俗易懂白话,来讲讲到底有什么好处

例如

接口类型 变量 = new 实现类();

实现类类型 变量 = new 实现类();
8228 次点击
所在节点    Java
80 条回复
Caballarii
2019-08-16 14:57:12 +08:00
读一读 spring 就懂了
TobiahShaw
2019-08-16 15:05:11 +08:00
单实现类接口意义不大,多实现接口配合泛型或者其他特性很方便;另外这个接口定义和实现不是同一方做的;
janus77
2019-08-16 15:07:49 +08:00
给你一个场景:
父类定义变量:接口类 a
子类重写:a=实现类。
onice
2019-08-16 15:09:35 +08:00
方便代码重用。接口是对类功能的抽象,List<> list = new ArrayList<>()和 List<> list = new LinkedList<>(),他们的功能都是一样的,但是实现不一样。在实际开发中,例如 DAO 层,通过定义接口,可以写好几套实现。例如对于 mysql 的一套,对于 oracle 的一套。
qiyuey
2019-08-16 15:11:55 +08:00
new 对象时接收没必要用接口,因为 new 所在的方法肯定是强感知的,主要使用接口的是模板方法、工具类等,关注的是实现了接口的一类对象。
Wangjl
2019-08-16 15:15:13 +08:00
new 对象时用其接口类型变量来接收,这么做的好处是
1.统一规范
例如:我定义了一个接口,里面有 a、b、c 三个方法
然后别人实现了该接口
如果我用
实现类 对象名 = new 实现类 这种方式 new 对象
此时如果实现类中还定义了 d 方法,就可以通过该对象调用 d 方法
如果此时是多人开发的场景,我们发现实现类 d 方法有问题。然后别人就重写了该实现类,例如删除或修改了 d 方法
此时调用 d 方法的地方,我们全部都需要删除或修改

而如果是用接口作为接收类型创建的对象:
本身该接口就是我们定义的,我们明确知道我这个功能只需要 a、b、c 这三个方法
这时候,你拿去实现。你实现类中还定义了一个 d 方法
但我 new 对象的时候,用接口来接收,这样我就不管你实现类里实现了什么特有方法,我都不关心
我用接口接收,这样就只能调用我接口中定义的 a、b、c 三个方法,这样以后你实现类再改变
我都不需要去改变我的对象的调用
这样做的好处就是,限定只能调用接口中的方法。

不知道我这么理解对不对。
fkdog
2019-08-16 15:18:59 +08:00
如果你有做过框架的话,你会体会到多态给你带来的好处。
在实际业务开发里,这种方式的编码带来的好处比较有限。
大部分其实也是大家约定俗成保留了这种方式。
cwjokaka
2019-08-16 15:19:03 +08:00
对某个对象,调用层知道的信息越少越好。最浅显的作用:实现类类型 变量 = new 实现类(); 至少改代码只需改 new 的那部分,而不用改类型🤪
Raymon111111
2019-08-16 15:20:46 +08:00
简单讲用的时候可以统一处理. 明白不了有什么好处是因为看的例子确实不好. 我这里举一个例子我遇到实际工程里的例子吧.

比如现在工程调用算法开发的类实现算法功能, 不同的功能有不同的算法. 有 A1 算法, A2 算法. 工程并不想知道算法的细节, 也不关心算法是哪来的, 最简单的方案是算法提供单一 API, 比如叫做 Algor.compute(input). 工程只要在 input 上约定想用某个算法, 这个内部自动就找到对应的算法.

那这里算法去真正实现 Algor.compute 的时候这个接口肯定是通用的, 类似下面这样的逻辑.
Algor.compute() {
A a = getAlgor(input); // 通过 input 拿到真正 A1 或者 A2 或者其它的算法
a.compute(); // 通用的算法实现接口, A1, A2 都去实现 A, 算法真正的实现写在 compute 里
}

那这个 A 就是 A1, A2 的接口类. 可以看到 Algor.compute 这个方法只用到 A 这个接口类.
wysnylc
2019-08-16 15:21:16 +08:00
@fkdog #27 说得对,写业务是用不太到接口的,框架和工具会比较多使用接口的多实现来实现功能实现的替换(有点绕哈哈
otakustay
2019-08-16 15:26:58 +08:00
让后面用这个对象的人别瞎 JB 乱用,方便以后在符合左边接口的前提下改了实际右边的类型后不会一片错
Wangjl
2019-08-16 15:28:47 +08:00
@otakustay 哈哈哈,好像是这么回事。
pisc
2019-08-16 15:45:16 +08:00
我来唱反调好了,大部分情况下没用,楼上张嘴闭嘴多态,用实现类接收就不能用多态了?你们懂个毛的 polymorphism,这种滥用 subtyping polymorphism 除了会丢失类型精度,还有什么意义?根本上和静态类型的动机是相违背的
doneself
2019-08-16 15:46:05 +08:00
有几个用处,一来某人定义了接口规范,后面其他人实现类的功能,就能保证他的方法名一致。
还有就是多态更规范一点。例如我定义一个 ExcelHelper 的工具类。删除一个单元格在 97 跟 2003、2007 版本实现可能都不一样。
那么就可以 定义一个接口 IExcelHelper,定义一个 DeleteCell()的方法。
调用者就不用考虑每次都用哪个实现类。初始化的时候,直接。
IExcelHelper Helper = new Helper97();
或者 IExcelHelper Helper = new Helper03();
再或者 IExcelHelper Helper = new Helper07();

调用者就不用关心实现,无论是 97,03,07 调用都是 IExcelHeler.DeteleCell()。
反正就是 规范 、 分离的原则。
xiangyuecn
2019-08-16 15:46:42 +08:00
在真真实实需要用到接口的地方,硬是不用接口,那么只能用反射。不像弱类型语言,随便给个对象,.xxx()随便调,只要你定义了 xxx😒 不单是 java,其他有接口的语言都是这个样子吧。
pisc
2019-08-16 15:51:47 +08:00
还有什么类型变动不用改类型什么的,最简单的做法你可以连左边的类型都不用写,直接用 var,让编译器给你推导出类型就 OK 了
AsiaToyo
2019-08-16 15:55:14 +08:00
父類引用指向子類對象
vincel
2019-08-16 16:00:35 +08:00
接口的意义是松耦合,通俗一点来讲就是:"不管接口的实现类怎么改,只要你实现了这个接口,我就可以调用你,屏蔽掉其他所有不必要因素。"
举个例子:假如你负责的模块 A 要处理数据,而具体处理方法是另一个小组模块 B 实现的,他们提供给你的类叫 MyTask,用 getTask 方法返回给你。所以你的代码是:
Runnable r = ModuleB.getTask();
//你的模块有一万个方法要用到这个对象
functionA(Runnable r)
functionB(Runnable r)
....
以后不管他的实现类怎么改怎么换,只要他实现了 runnable 接口,你都不用管。但如果你是用 MyTask 声明的对象,那么只要他们以后把实现类换成 TaskB 了或者换改类名了,那么你那一万处代码全部要改。
MaxTan
2019-08-16 16:09:34 +08:00
光你给出的例子那种写法没啥卵用;
但同一接口有多实现或者做类型约束类似场景,接口就非常有用;其实接口就是为了实现面向对象里面的多态性

大白话就是电脑有 usb 插口(声明一个 usb 接口类型),不管你插入的设备(对象)是鼠标还是键盘,只要它们带 usb 头(实现 USB 接口)就行了,好处就是光一个 usb 接口可以插 u 盘、鼠标、手柄、音响....
其他电脑配件同理,只要有接口标准,配置一切皆可选。 这就是多态装机
tedcon
2019-08-16 16:20:34 +08:00
依赖倒转原则看一下

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/592462

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX