自动装箱,拆箱和NoSuchMethodError
J2SE 5為Java編程語言引入了許多功能。 這些功能之一是自動裝箱和拆箱 ,這是我?guī)缀趺刻於紱]有考慮過的功能。 它通常很方便(尤其是與收藏夾一起使用時),但有時會導(dǎo)致一些令人討厭的驚喜 ,即“ 怪異 ”和“ 瘋狂” 。 在此博客文章中,我介紹了一種罕見的NoSuchMethodError案例(但對我來說很有趣),該案例是由于在自動裝箱/拆箱之前將使用Java版本編譯的類與包含自動裝箱/取消裝箱的Java版本編譯的類混合在一起而造成的。
下一個代碼清單顯示了一個簡單的Sum類,該類可以在J2SE 5之前編寫。它已重載了“ add”方法,這些方法接受不同的原始數(shù)值數(shù)據(jù)類型,并且Sum>每個實例Sum>簡單地添加了通過以下任何一種方式提供給它的所有數(shù)字類型其重載的“添加”方法。
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類的所有客戶端都需要向這些“添加”方法提供原語,或者,如果它們具有與原語相同的引用,則需要在將其中一個引用稱為“添加”方法。 在調(diào)用這些方法之前,在客戶端代碼上有責(zé)任從引用類型轉(zhuǎn)換為相應(yīng)的原始類型。 下一個代碼清單中顯示了如何完成此操作的示例。
不取消裝箱:客戶端將引用轉(zhuǎn)換為基元
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的自動裝箱和拆箱功能旨在解決這種情況下所需的額外工作 。 通過取消裝箱,客戶端代碼可以使用與預(yù)期的基本類型相對應(yīng)的引用類型來調(diào)用上述“添加”方法,并且這些引用將自動“取消裝箱”為原始形式,以便可以調(diào)用適當(dāng)?shù)摹疤砑印狈椒ā?Java語言規(guī)范的 第5.1.8節(jié) (“取消裝箱轉(zhuǎn)換”)說明了提供的數(shù)字引用類型在取消裝箱中將轉(zhuǎn)換為哪些原語,該規(guī)范的 第5.1.7節(jié) (“裝箱轉(zhuǎn)換”)列出了自動裝箱的引用類型。從自動裝箱中的每個原語。
在此示例中,在調(diào)用Sum的“ add”方法之前,將引用類型轉(zhuǎn)換為對應(yīng)的原始對等類型,從而使客戶方面的拆箱工作減少了,但并沒有使客戶完全不必在提供它們之前處理數(shù)字值。 因為引用類型可以為null ,所以客戶端可以為Sum的“ add”方法之一提供null引用,并且當(dāng)Java嘗試自動將null取消裝箱到其對應(yīng)的原語時,將引發(fā)NullPointerException 。 下一個代碼清單從上面進行了改編,以指示在客戶端不再需要將引用轉(zhuǎn)換為原語,但是仍然需要檢查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(); }在設(shè)計API時,可能需要避免客戶端代碼在Sum上調(diào)用“ add”方法之前檢查其引用是否為null。 消除需求的一種方法是更改??“添加”方法以顯式接受引用類型,而不是原始類型。 然后, Sum類可以在顯式或隱式(取消裝箱)對它進行解引用之前檢查null。 接下來顯示了經(jīng)過修改的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”方法,而不必擔(dān)心傳入的引用是否為null。 但是,如果涉及的任何一個類(客戶端類或Sum類的一個版本)都使用不同版本的Java編譯,則像這樣對Sum類的API進行更改可能會導(dǎo)致NoSuchMethodError 。 特別是,如果客戶端代碼使用原語并且使用JDK 1.4或更早版本進行編譯,并且Sum類是所示的最新版本(期望使用引用代替原語)并且使用J2SE 5或更高版本進行了編譯,則將遇到類似以下內(nèi)容的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 (預(yù)拆箱),并且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開發(fā)人員的一些觀察和提醒。
- 類路徑很重要:
- 使用相同版本的Java(相同的-source和-target )編譯的Java .class文件可以避免本文中的特定問題。
- 自動裝箱和取消裝箱的目的是很好的,并且通常非常方便,但是如果在一定程度上不牢記,可能會導(dǎo)致令人驚訝的問題。 在這篇文章中,仍然需要檢查空值(或知道對象不是空值),這是由于拆箱而導(dǎo)致隱式解引用的情況。
- 是否允許客戶端傳遞null并讓服務(wù)類代表它們檢查null是API風(fēng)格的問題。 在工業(yè)應(yīng)用程序中,我將用每個方法的Javadoc注釋中的@param聲明每個“添加”方法參數(shù)是否允許為null。 在其他情況下,可能要讓調(diào)用者負責(zé)確保任何傳入的引用都不為空,并且如果調(diào)用者不遵守該約定,則拋出NullPointerException內(nèi)容將是滿意的(也應(yīng)在方法的Javadoc)。
- 盡管通常會在完全刪除某個方法或在該方法可用之前訪問舊類或方法的API在類型或類型數(shù)方面發(fā)生更改時看到NoSuchMethodError 。 在Java自動裝箱和拆箱在很大程度上被視為理所當(dāng)然的日子里,很容易想到將方法從采用原語轉(zhuǎn)換為采用相應(yīng)的引用類型不會產(chǎn)生任何影響,但是即使這種更改也會導(dǎo)致異常,如果并非所有涉及的類都基于支持自動裝箱和拆箱的Java版本構(gòu)建。
- 確定要針對其編譯特定.class文件的Java版本的一種方法是使用javap -verbose并在javap輸出中查找“主要版本:”。 在本文示例中使用的類(針對JDK 1.4和Java SE 8編譯)中,“ 主要版本 ”條目分別為48和52( Java類文件上Wikipedia條目的“ 常規(guī)布局”部分列出了主要版本) )。
幸運的是,由于構(gòu)建通常會清理所有工件并在相對連續(xù)的基礎(chǔ)上重建代碼,因此本文中使用示例和文本演示的問題并不常見。 但是,在某些情況下可能會發(fā)生這種情況,最可能的情況之一是意外使用舊的JAR文件時,因為它位于運行時類路徑上的等待中。
翻譯自: https://www.javacodegeeks.com/2014/08/autoboxing-unboxing-and-nosuchmethoderror.html
總結(jié)
以上是生活随笔為你收集整理的自动装箱,拆箱和NoSuchMethodError的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 介绍JBoss BPM Suite安装程
- 下一篇: 国际接吻日是几月几号 国际亲吻节是几月几