有人告诉我,针对局部变量的接口(interface)编程是无用的,不应该这样做,因为它只会损害性能而没有任何好处。
public void foo() {
ArrayList<Integer> numbers = new ArrayList<Integer>();
// do list-y stuff with numbers
}
代替
public void foo() {
List<Integer> numbers = new ArrayList<Integer>();
// do list-y stuff with numbers
}
我觉得性能影响可以忽略不计,但不可否认的是,使用 ArrayList 的列表语义并没有太大好处。有充分的理由选择一种方式吗?
最佳答案
以这个类为例:
public class Tmp {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
}
}
它编译成这样:
Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokevirtual #5; //Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
16: pop
17: return
}
虽然这门课:
public class Tmp {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
}
}
编译成这样:
Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2; //class java/util/ArrayList
3: dup
4: invokespecial #3; //Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: iconst_1
10: invokestatic #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
13: invokeinterface #5, 2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
18: pop
19: return
}
你会看到唯一的区别是第一个(使用ArrayList)调用invokevirtual,另一个(使用List 使用)调用接口(interface)。 invokeinterface 实际上比 invokevirtual 慢一点点(根据 this guy 约 38%)。这是 apparently由于 JVM 在搜索具体类的虚方法表而不是接口(interface)的方法表时可以进行优化。所以你说的实际上是真的。接口(interface)调用比具体类调用慢。
但是,您必须考虑我们所谈论的速度类型。对于 100,000,000 次调用,实际差异为 0.03 秒。因此,您必须进行大量调用才能真正显着降低速度。
另一方面,正如@ChinBoon 指出的那样,对接口(interface)进行编码使那些使用您的代码的人更加容易,尤其是当您的代码返回某种List 时。因此,在绝大多数情况下,编码的便利性远远超过了相对性能开销。
在阅读@MattQuigley 的评论并在开车回家的路上思考之后添加
这意味着您不必为此担心太多。任何性能提升或损失都可能非常很小。
请记住,为您的返回类型和方法参数使用接口(interface)是一个非常好的主意。这允许您和任何使用您的代码的人使用最适合他们需要的 List 实现。我的示例还表明,如果您碰巧使用返回 List 的方法,那么在 99% 的情况下,您不最好将它强制转换为具体类,只是为了获得性能提升。转换所花费的时间可能会超过性能的提高。
话虽这么说,这个例子也表明,对于一个本地变量,你确实最好使用一个具体的类而不是一个接口(interface)。如果您使用的唯一方法是 List 上的方法,那么您可以切换到没有副作用的实现类。另外,如果需要,您可以访问特定于实现的方法。此外还有轻微的性能提升。
tl;dr
始终对方法的返回类型和参数使用接口(interface)。为局部变量使用具体类是个好主意。如果您使用的唯一方法是在接口(interface)上,它会带来较小的性能提升,并且无需切换实现。最后,你不应该太担心它。 (返回类型和参数除外。这很重要。)
关于java - 'programming to interfaces' 的最佳实践是否适用于局部变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10627750/
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou
给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje
我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i
我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer
在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',
我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun