自动装箱,拆箱和NoSuchMethodError

J2SE 5为Java编程语言引入了许多功能。 这些功能之一是自动装箱和拆箱 ,这是我几乎每天都没有考虑过的功能。 它通常很方便(尤其是与收藏夹一起使用时),但有时会导致一些令人讨厌的惊喜 ,即“ 怪异 ”和“ 疯狂” 。 在此博客文章中,我介绍了一种罕见的NoSuchMethodError案例(但对我来说很有趣),该案例是由于在自动装箱/拆箱之前将使用Java版本编译的类与包含自动装箱/取消装箱的Java版本编译的类混合在一起而造成的。

下一个代码清单显示了一个简单的Sum类,该类可以在J2SE 5之前编写。它已重载了“ add”方法,这些方法接受不同的原始数值数据类型,并且Sum>每个实例Sum>简单地添加了通过以下任何一种方式提供给它的所有数字类型其重载的“添加”方法。

Sum.java(J2SE 5之前的版本)

import java.util.ArrayList;public class Sum
{private double sum = 0;public void add(short newShort){sum += newShort;}public void add(int newInteger){sum += newInteger;}public void add(long newLong){sum += newLong;}public void add(float newFloat){sum += newFloat;}public void add(double newDouble){sum += newDouble;}public String toString(){return String.valueOf(sum);}
}

在无法进行拆箱之前,上述Sum类的所有客户端都需要向这些“添加”方法提供原语,或者,如果它们具有与原语相同的引用,则需要在将其中一个引用称为“添加”方法。 在调用这些方法之前,在客户端代码上有责任从引用类型转换为相应的原始类型。 下一个代码清单中显示了如何完成此操作的示例。

不取消装箱:客户端将引用转换为基元

private static String sumReferences(final Long longValue, final Integer intValue, final Short shortValue)
{final Sum sum = new Sum();if (longValue != null){sum.add(longValue.longValue());}if (intValue != null){sum.add(intValue.intValue());}if (shortValue != null){sum.add(shortValue.shortValue());}return sum.toString();
}

J2SE 5的自动装箱和拆箱功能旨在解决这种情况下所需的额外工作 。 通过取消装箱,客户端代码可以使用与预期的基本类型相对应的引用类型来调用上述“添加”方法,并且这些引用将自动“取消装箱”为原始形式,以便可以调用适当的“添加”方法。 Java语言规范的 第5.1.8节 (“取消装箱转换”)说明了提供的数字引用类型在取消装箱中将转换为哪些原语,该规范的 第5.1.7节 (“装箱转换”)列出了自动装箱的引用类型。从自动装箱中的每个原语。

boxingAndUnboxing_transparentbg

在此示例中,在调用Sum的“ add”方法之前,将引用类型转换为对应的原始对等类型,从而使客户方面的拆箱工作减少了,但并没有使客户完全不必在提供它们之前处理数字值。 因为引用类型可以为null ,所以客户端可以为Sum的“ add”方法之一提供null引用,并且当Java尝试自动将null取消装箱到其对应的原语时,将引发NullPointerException 。 下一个代码清单从上面进行了改编,以指示在客户端不再需要将引用转换为原语,但是仍然需要检查null以避免NullPointerException 。

自动取消装箱秘密对原始的引用:仍然必须检查是否为空

private static String sumReferences(final Long longValue, final Integer intValue, final Short shortValue)
{final Sum sum = new Sum();if (longValue != null){sum.add(longValue);}if (intValue != null){sum.add(intValue);}if (shortValue != null){sum.add(shortValue);}return sum.toString();
}

在设计API时,可能需要避免客户端代码在Sum上调用“ add”方法之前检查其引用是否为null。 消除需求的一种方法是更改​​“添加”方法以显式接受引用类型,而不是原始类型。 然后, Sum类可以在显式或隐式(取消装箱)对它进行解引用之前检查null。 接下来显示了经过修改的Sum类,其中包含已更改的,更易于客户端使用的API。

用“ add”方法求和的类期望引用而不是基元

import java.util.ArrayList;public class Sum
{private double sum = 0;public void add(Short newShort){if (newShort != null){sum += newShort;}}public void add(Integer newInteger){if (newInteger != null){sum += newInteger;}}public void add(Long newLong){if (newLong != null){sum += newLong;}}public void add(Float newFloat){if (newFloat != null){sum += newFloat;}}public void add(Double newDouble){if (newDouble != null){sum += newDouble;}}public String toString(){return String.valueOf(sum);}
}

修改后的Sum类对客户端更友好,因为它允许客户端将引用传递给它的任何“ add”方法,而不必担心传入的引用是否为null。 但是,如果涉及的任何一个类(客户端类或Sum类的一个版本)都使用不同版本的Java编译,则像这样对Sum类的API进行更改可能会导致NoSuchMethodError 。 特别是,如果客户端代码使用原语并且使用JDK 1.4或更早版本进行编译,并且Sum类是所示的最新版本(期望使用引用代替原语)并且使用J2SE 5或更高版本进行了编译,则将遇到类似以下内容的NoSuchMethodError (“ S”表示它是“ add”方法,它期望原始short ,而“ V”表示该方法返回void )。

Exception in thread "main" java.lang.NoSuchMethodError: Sum.add(S)Vat Main.main(Main.java:9)

另一方面,如果客户端使用J2SE 5或更高版本进行编译,并且如第一个示例中那样将原始值提供给Sum (预拆箱),并且Sum类在JDK 1.4或更早版本中使用“ add”方法进行编译原语,会遇到不同版本的NoSuchMethodError 。 请注意,此处引用了Short参考。

Exception in thread "main" java.lang.NoSuchMethodError: Sum.add(Ljava/lang/Short;)Vat Main.main(Main.java:9)

由此可见对Java开发人员的一些观察和提醒。

  • 类路径很重要:
    • 使用相同版本的Java(相同的-source-target )编译的Java .class文件可以避免本文中的特定问题。
  • 自动装箱和取消装箱的目的是很好的,并且通常非常方便,但是如果在一定程度上不牢记,可能会导致令人惊讶的问题。 在这篇文章中,仍然需要检查空值(或知道对象不是空值),这是由于拆箱而导致隐式解引用的情况。
  • 是否允许客户端传递null并让服务类代表它们检查null是API风格的问题。 在工业应用程序中,我将用每个方法的Javadoc注释中的@param声明每个“添加”方法参数是否允许为null。 在其他情况下,可能要让调用者负责确保任何传入的引用都不为空,并且如果调用者不遵守该约定,则抛出NullPointerException内容将是满意的(也应在方法的Javadoc)。
  • 尽管通常会在完全删除某个方法或在该方法可用之前访问旧类或方法的API在类型或类型数方面发生更改时看到NoSuchMethodError 。 在Java自动装箱和拆箱在很大程度上被视为理所当然的日子里,很容易想到将方法从采用原语转换为采用相应的引用类型不会产生任何影响,但是即使这种更改也会导致异常,如果并非所有涉及的类都基于支持自动装箱和拆箱的Java版本构建。
  • 确定要针对其编译特定.class文件的Java版本的一种方法是使用javap -verbose并在javap输出中查找“主要版本:”。 在本文示例中使用的类(针对JDK 1.4和Java SE 8编译)中,“ 主要版本 ”条目分别为48和52( Java类文件上Wikipedia条目的“ 常规布局”部分列出了主要版本) )。

幸运的是,由于构建通常会清理所有工件并在相对连续的基础上重建代码,因此本文中使用示例和文本演示的问题并不常见。 但是,在某些情况下可能会发生这种情况,最可能的情况之一是意外使用旧的JAR文件时,因为它位于运行时类路径上的等待中。

翻译自: https://www.javacodegeeks.com/2014/08/autoboxing-unboxing-and-nosuchmethoderror.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/362423.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue 不能监测数组长度变化length的原因

由于 JavaScript 的限制,Vue 不能检测以下变动的数组: 当你利用索引直接设置一个项时,例如: vm.items[indexOfItem] newValue 当你修改数组的长度时,例如: vm.items.length newLength因为vue的响应式是通…

vue 动态显示三级路由

无需 vuex、本地存储实现动态显示三级路由。 目录 一、需求描述: 二、代码 2.1 路由配置 1. 我的一级菜单和二级菜单的路由配置的: 2. 三级菜单的路由配置: 3. 上面有几个变量和要注意的细节: 2.2 封装导航栏 2.3 封装面…

第二章 Vue快速入门-- 18 v-for中key的使用注意事项

注意:如果属性和方法还没定义直接使用的话&#xff0c;就会报 xxx is not defined 导致界面不能正常显示。我看视频教程里老师的可以直接使用&#xff0c;而且界面正常显示&#xff0c;可能是vue版本不同吗&#xff1f;还不清楚 1 <!DOCTYPE html>2 <html lang"…

口碑网发布2010吃住玩消费风向指数

当在淘宝网上买衣服、买电器、买……都变得司空见惯&#xff0c;下一步我们会在网上完成什么消费&#xff1f; 通过网购等电子商务模式的带动&#xff0c;电子商务平台对于老百姓日常消费、生活的影响逐渐深化。原本更多依靠传统手段完成的吃住玩等生活消费&#xff0c;现在也…

Java并发教程–线程安全设计

在回顾了处理并发程序时的主要风险&#xff08;如原子性或可见性 &#xff09;之后&#xff0c;我们将进行一些类设计&#xff0c;以帮助我们防止上述错误。 其中一些设计导致了线程安全对象的构造&#xff0c;从而使我们可以在线程之间安全地共享它们。 作为示例&#xff0c;我…

ES6 iterator 迭代器

iterator使用TypeScript 的描述&#xff1a; interface Iterable {[Symbol.iterator]() : Iterator,}interface Iterator {next(value?: any) : IterationResult,}interface IterationResult {value: any,done: boolean,} 一个数据结构只要具有 Symbol.iterator属性&#xff0…

vue 后端返回二进制流文件,前端如何实现下载?

目录 1. axios 请求二进制流文件导出文件 1.1 后台返回的二进制流&#xff1a; 1.2 使用&#xff1a; 1.3 需要注意以下几点&#xff1a; 2. 关于 arraybuffer 和 blob 2.1 ArrayBuffer 和 blob 2.2 区别 2.3 相互转换 3. 主要参考&#xff1a; 1. axios 请求二进制…

python量化之路:获取历史某一时刻沪深上市公司股票代码及上市时间

python量化之路&#xff1a;获取历史某一时刻沪深上市公司股票代码及上市时间 最近开始玩股票量化&#xff0c;由于想要做完整的股票回测&#xff0c;因此股票的上市和退市信息就必不可少。因为我们回测的时候必须要知道某一日期沪深股票的成分包含哪些对吧。所以我们要把沪深全…

《网页设计创意书》读后感

刚刚收到《网页设计创意书》&#xff0c;确实有点惊喜&#xff0c;开始以为是像之前审读的书一样是一叠叠的打印纸&#xff0c;没想到是一本成品书&#xff0c;拿到手上沉甸甸的&#xff0c;随便翻看了一下&#xff0c;没想到里面竟然还是全彩页印刷的&#xff0c;本书的第一感…

游戏AI –行为树简介

游戏AI是一个非常广泛的主题&#xff0c;尽管有很多资料&#xff0c;但我找不到能以较慢&#xff0c;更容易理解的速度缓慢介绍这些概念的东西。 本文将尝试解释如何基于行为树的概念来设计一个非常简单但可扩展的AI系统。 什么是AI&#xff1f; 人工智能是参与游戏的实体表现…

js 常用类型转换简写

1、字符串转数字 666// 666 2、转换为字符串 666//666 更多专业前端知识&#xff0c;请上 【猿2048】www.mk2048.com

mockJs文档(二)

mockJs官方文档 mockJs文档&#xff08;一&#xff09; 目录 1. Mock.mock( rurl?, rtype?, template|function( options ) ) 1.1 Mock.mock( template ) 1.2 Mock.mock( rurl, template ) 1.3 Mock.mock( rurl, function( options ) ) 1.4 Mock.mock( rurl, r…

winforms中限定上传文件类型

获取文件路径string fileExtension System.IO.Path.GetExtension(filePath).ToLower();bool flag true;string[] AllowExtension { ".doc", ".xls", ".ppt", ".docx", ".xlsx", ".pptx", ".txt", &q…

liteos错误处理(十一)

1. 概述 1.1 基本概念 错误处理指用户代码发生错误时&#xff0c;系统调用错误处理模块的接口函数&#xff0c;完成上报错误信息&#xff0c;并调用用户自己的钩子函数&#xff0c;进行特定的处理。 错误处理模块实现OS内部错误码记录功能。OS内部错误码无法通过接口返回&#…

这是东西:jUnit:动态测试生成

当您需要在许多不同的输入值或配置上运行同一组测试时&#xff0c;动态测试生成很有用。 可以使用参数化测试或使用理论来实现。 当您有大量数据用作参数并想对所有组合进行测试时&#xff0c;这些理论非常有用。 您得到的控制较少&#xff0c;但是您不必自己编写合并和迭代的…

js 变量提升与函数提升

规则&#xff1a; 函数的提升优先于变量提升。同名的函数会覆盖同名的函数与变量。同名的变量不会覆盖同名的函数。 示例代码1&#xff1a; <!DOCTYPE html><html lang"zh"><head><meta charset"UTF-8" /><meta name"vi…

mockJs文档(一)

Mock.js 官网 目录 1. 开始安装 1.1 Node&#xff08;CommonJS&#xff09; 1.2 CMD方式 2. 语法规范 2.1 数据模板定义规范 DTD 2.1.1. 属性值是字符串 String 2.1.2. 属性值是数字 Number 2.1.3. 属性值是布尔型 Boolean 2.1.4. 属性值是对象 Object 2.1.5. 属性值…

【JOURNAL】《不思八九》 --和友腊八诗一首

不思八九旧岁新醅雪&#xff0c;腊八数九粥。红泥杜康曲&#xff0c;暖腹亦无忧。&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;&#xff0d;http://t.sina.com.cn/n/博客顺风 腊八 辞岁逢新雪&#xff0c;好煮腊八粥。且将情入味&#xff0c;一并…

团队测试计划

我们是否需要测试&#xff0c;直到我们的软件是完美的&#xff1f; 首先针对第一个问题&#xff0c;我们一直觉得有必要&#xff0c;因为老师说过&#xff0c;只有多次测试才能真正检测出自己的代码是否完全没问题&#xff0c;所以我们进行了多次测试&#xff0c;虽然我对自己的…

ChoiceFormat:数字范围格式

ChoiceFormat类的Javadoc声明ChoiceFormat “允许您将格式附加到一定范围的数字上”&#xff0c;并且“通常在MessageFormat中用于处理复数”。 这篇文章描述了java.text.ChoiceFormat并提供了一些在Java代码中应用它的示例。 ChoiceFormat与java.text包中其他“ 格式 ”类之间…