小心重載API方法

重載方法是API設計中的重要概念,尤其是當您的API是流利的API或DSL( 特定于域的語言 )時。

對于jOOQ就是這種情況,在這種情況下,您經常想使用與完全相同的方法名稱來與庫進行各種交互。






示例:jOOQ條件

package org.jooq;public interface Condition {// Various overloaded forms of the "AND" operation:Condition and(Condition other);Condition and(String sql);Condition and(String sql, Object... bindings);// [...]}

所有這些方法都使用“ AND”運算符將兩個條件相互關聯。 理想情況下,實現相互依賴,從而造成單點故障。 這會使事情變干 :

package org.jooq.impl;abstract class AbstractCondition implements Condition {// The single point of failure@Overridepublic final Condition and(Condition other) {return new CombinedCondition(Operator.AND, Arrays.asList(this, other));}// "Convenience methods" delegating to the other one@Overridepublic final Condition and(String sql) {return and(condition(sql));}@Overridepublic final Condition and(String sql, Object... bindings) {return and(condition(sql, bindings));}}

泛型和重載的麻煩

當使用Eclipse開發時,Java 5世界似乎比實際情況更加光彩照人。 Varargs和泛型作為Java 5中的語法糖引入。它們在JVM中并不是真的存在。 這意味著編譯器必須正確鏈接方法調用,在需要時推斷類型,并在某些情況下創建綜合方法。 根據JLS( Java語言規范 ),當在重載方法中使用varargs / generics時,存在很多歧義。

讓我們詳細介紹一下泛型:

在jOOQ中要做的一件好事是將常量值與字段一樣對待。 在許多地方,字段參數像這樣重載:

// This is a convenience method:public static <T> Field<T> myFunction(Field<T> field, T value) {return myFunction(field, val(value));}// It's equivalent to this one.public static <T> Field<T> myFunction(Field<T> field, Field<T> value) {return MyFunction<T>(field, value);}

在大多數情況下,上面的方法效果很好。 您可以像這樣使用上述API:

Field<Integer> field1  = //...Field<String>  field2  = //...Field<Integer> result1 = myFunction(field1, 1);Field<String>  result2 = myFunction(field2, "abc");

但是,當<T>綁定到對象時,就會出現麻煩!

// While this works...Field<Object>  field3  = //...Field<Object>  result3 = myFunction(field3, new Object());// ... this doesn't!Field<Object>  field4  = //...Field<Object>  result4 = myFunction(field4, field4);Field<Object>  result4 = myFunction(field4, (Field) field4);Field<Object>  result4 = myFunction(field4, (Field<Object>) field4);

當<T>綁定到Object時,兩種方法突然都適用,并且根據JLS,它們都不是更具體的! 盡管Eclipse編譯器通常比較寬容(并且在這種情況下直觀地鏈接了第二個方法),但是javac編譯器不知道該調用要做什么。 而且沒有辦法解決。 您不能將field4強制轉換為Field或Field <Object>強制鏈接器鏈接至第二種方法。 對于API設計人員來說,這是個壞消息。

有關此特殊情況的更多詳細信息,請考慮以下堆棧溢出問題,我將此問題報告給Oracle和Eclipse。 讓我們看看哪種編譯器實現是正確的:
http://stackoverflow.com/questions/5361513/reference-is-ambiguous-with-generics

靜態導入的麻煩,varargs

Varargs是Java 5中引入的另一個重要功能。盡管它只是語法糖,但在將數組傳遞給方法時可以節省很多代碼:

// Method declarations with or without varargspublic static String concat1(int[] values);public static String concat2(int... values);// The above methods are actually the same.String s1 = concat1(new int[] { 1, 2, 3 });String s2 = concat2(new int[] { 1, 2, 3 });// Only, concat2 can also be called like this, convenientlyString s3 = concat2(1, 2, 3);

那是眾所周知的。 它與原始類型數組的工作方式與與Object []相同。 它也可以與T []一起使用,其中T是泛型類型!

// You can now have a generic type in your varargs parameter:public static <T> T[] array(T... values);// The above can be called "type-safely" (with auto-boxing):Integer[] ints   = array(1, 2, 3);String[] strings = array("1", "2", "3");// Since Object could also be inferred for T, you can even do this:Object[] applesAndOranges = array(1, "2", 3.0);

最后一個例子實際上已經暗示了這個問題。 如果T沒有任何上限,則類型安全性完全消失。 這是一種錯覺,因為最后,總是可以將varargs參數推斷為“ Object…”。 這就是當您重載此類API時這會引起麻煩的方式。

// Overloaded for "convenience". Let's ignore the compiler warning// caused when calling the second methodpublic static <T> Field<T> myFunction(T... params);public static <T> Field<T> myFunction(Field<T>... params);

起初,這看起來像個好主意。 參數列表可以是常量值(T…)或動態字段(Field…)。 因此,原則上,您可以執行以下操作:

// The outer function can infer Integer for <T> from the inner// functions, which can infer Integer for <T> from T...Field<Integer> f1 = myFunction(myFunction(1), myFunction(2, 3));// But beware, this will compile too!Field<?> f2 = myFunction(myFunction(1), myFunction(2.0, 3.0));

內部函數將推斷<T>的Integer和Double。 對于不兼容的返回類型Field <Integer>和Field <Double>,帶有“ Field <T> ...”參數的“打算”方法不再適用。 因此,編譯器將帶有“ T…”的方法一鏈接為唯一適用的方法。 但是您不會猜測<T>的(可能)推斷范圍。 這些是可能的推斷類型:

// This one, you can always do:Field<?> f2 = myFunction(myFunction(1), myFunction(2.0, 3.0));// But these ones show what you're actually about to doField<? extends Field<?>>                       f3 = // ...Field<? extends Field<? extends Number>>        f4 = // ...Field<? extends Field<? extends Comparable<?>>> f5 = // ...Field<? extends Field<? extends Serializable>>  f6 = // ...

編譯器可以推斷出Field <? 將Number&Comparable <?>和Serializable>擴展為<T>的有效上限。 但是,<T>沒有有效的精確界限。 因此,必要的<? 擴展[上限]>。

結論

將varargs參數與泛型結合使用時要特別小心,尤其是在重載方法中。 如果用戶將通用類型參數正確綁定到您想要的目標,則一切正常。 但是,如果有一個拼寫錯誤(例如,將Integer與Double混淆),那么您的API用戶就注定了。 而且他們不會輕易發現自己的錯誤,因為沒有人能讀懂這樣的編譯器錯誤消息:

Test.java:58: incompatible types
found   : Test.Field<Test.Field<? extends java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>>>
required: Test.Field<java.lang.Integer>Field<Integer> f2 = myFunction(myFunction(1),myFunction(2.0, 3.0));

參考:在JAVA,SQL和JOOQ博客上,我們的JCG合作伙伴 Lukas Eder 謹慎使用了重載API方法 。

相關文章 :

  • Java中的數據庫架構導航
  • ORM問題
  • Java泛型快速教程
  • 使用Spring和Java泛型簡化數據訪問層

翻譯自: https://www.javacodegeeks.com/2011/12/overload-api-methods-with-care.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373983.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373983.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373983.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

phpcms 下載模型列表頁直接點擊下載

下載模型設置本地下載 列表頁模板直接調用 <article class"prjDown"><p class"prjDownTitle">方案下載</p><nav class"prjDownNav"><ul>{pc:content action"lists" catid"$catid" num"3…

為什么Java中類方法不能訪問實例方法

我們已經知道類體中的方法分為實例方法和類方法兩種&#xff0c;用static修飾的是類方法。二者有什么區別呢&#xff1f;當一個類創建了一個對象后&#xff0c;這個對象就可以調用該類的方法。 當類的字節碼文件被加載到內存時&#xff0c;類的實例方法不會被分配入口地址&…

python展開 c函數中的宏預處理_C中的預處理宏

C中的預處理宏宏定義就屬于預處理命令的一種。那么&#xff0c;什么是宏呢&#xff1f;宏&#xff1a;c語言標準允許在程序中用一個標識符來表示一個字符串。標識符就是宏名。宏替換&#xff1a;宏替換就是宏定義。在編譯預處理中&#xff0c;將程序中所有的宏名用相應的字符串…

(轉) 中斷處理程序中斷服務例程

關于中斷處理程序和中斷服務例程ISR的區別及聯系&#xff0c;之前一直搞混&#xff0c;今天抽時間將兩者關系弄弄清楚。ok,下面進入主題。首先中斷處理程序(Interrupt Handler)和中斷服務例程ISR(Inerrupt Service Routine)是兩個不同的概念.簡單來說就是&#xff0c;一條中斷線…

使用SQL:2003 MERGE語句的奧術魔術

時不時地&#xff0c;由于以下任何原因&#xff0c;我們不得不將INSERT與UPDATE區分開來感到尷尬&#xff1a; 我們必須至少發表兩個聲明 我們必須考慮性能 我們必須考慮比賽條件 我們必須在[UPDATE; 如果UPDATE_COUNT 0 THEN INSERT]和[INSERT; 如果例外然后更新] 我們必…

Swing 學習小記

初學Swing一路問題&#xff0c;一路學習 問題一&#xff1a;JPanel中動態組件添加&#xff0c;刷新問題&#xff1f; 錯誤一&#xff1a;使用repaint()方法&#xff0c;以為可以刷新&#xff0c;可行不通。 錯誤繼續發生&#xff1a;還是使用repaint()方法&#xff0c;與之前不…

leetcode Spiral Matrix

題目連接 https://leetcode.com/problems/spiral-matrix/ Spiral Matrix Description Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. For example, Given the following matrix: [   [ 1, 2, 3 ],   [ 4, 5…

python學生類出不來中文_Python 這類看起來學習門檻低的語言,是否真的適合入門編程學習?...

Python(計算機程序設計語言)Python是一種跨平臺的計算機程序設計語言。 是一個高層次的結合了解釋性、編譯性、互動性和面向對象的腳本語言。最初被設計用于編寫自動化腳本(shell)&#xff0c;隨著版本的不斷更新和語言新功能的添加&#xff0c;越多被用于獨立的、大型項目的開…

克隆可序列化和不可序列化的Java對象

開發人員經常依靠3d方庫來避免重新發明輪子&#xff0c;尤其是在Java世界中&#xff0c;Apache和Spring這樣的項目如此盛行。 在處理這些框架時&#xff0c;我們通常很少或根本無法控制其類的行為。 這有時會導致問題。 例如&#xff0c;如果您想深度克隆不提供合適克隆方法的對…

2014編程之美資格賽

2014 編程之美挑戰賽 --- 資格賽真題 題目1 : 同構 時間限制:2000ms單點時限:1000ms內存限制:256MB描述 給定2個樹A和B&#xff0c;保證A的節點個數>B的節點個數。 現在你需要對樹A的邊進行二染色。 一個好的染色方案&#xff0c;指不存在一個樹A中的連通塊&#xff0c;同時…

JavaScript之面向對象學習六原型模式創建對象的問題,組合使用構造函數模式和原型模式創建對象...

一、仔細分析前面的原型模式創建對象的方法,發現原型模式創建對象,也存在一些問題&#xff0c;如下&#xff1a; 1、它省略了為構造函數傳遞初始化參數這個環節,結果所有實例在默認的情況下都將取得相同的屬性值&#xff0c;這還不是最大的問題&#xff01; 2、最大的問題是原型…

stand up meeting 12/11/2015

part組員今日工作工作耗時/h明日計劃工作耗時/hUI馮曉云完成單詞釋義熱度排序&#xff1b;允許用戶自主添加釋義&#xff1b;完成了button位置的修正&#xff08;finally&#xff09;和彈窗的美化&#xff1b; 6try the backup plan 6PDF Reader朱玉影 完成了pdf文件的打…

ssrf漏洞內網滲透_滲透技巧之SSRF

SSRF——服務端請求偽造&#xff0c;上一篇&#xff0c;我談到了CSRF客戶端請求偽造&#xff0c;這個是我們通過攻擊用戶&#xff0c;引誘客戶點擊我們偽造好的表單&#xff0c;從而達到我們攻擊的目的&#xff0c;是從客戶端發起的&#xff0c;那么SSRF服務端請求偽造當然是通…

引入故意緩存

幾周前&#xff0c;我參加了ThoughtWorks 技術雷達研討會。 我在ThoughtWorks工作了多年&#xff0c;想想是否有人知道這些人在軟件開發方面的發展趨勢。 在技??巧上帶有上升箭頭的數字中&#xff0c;第17位被稱為“周到緩存”。 和斯科特肖一起喝酒時&#xff0c;我問他是什…

(小議)面向對象

什么是面向對象&#xff1f;如果讓我理解&#xff0c;只有一句話&#xff1a;它是一個與面向過程相對的概念&#xff0c;是一種進化或者升級。人們所設計的程序幾乎都是線性思維&#xff0c;即一步一步往下執行。對于一個沒有人機交互的簡單程序來說&#xff0c;這是簡單易行的…

int類型究竟占幾個字節

最近在看深入理解計算機系統這本書&#xff0c;上面提到了在32位機器和64機器中int類型都占用4個字節。后來&#xff0c;查了The C Programming language這本書&#xff0c;里面有一句話是這樣的&#xff1a;Each compiler is free to choose appropriate sizes for its own ha…

python fieldnames_csvreader.fieldnames在python中未被識別為csv reader對象的屬性

我試圖使用CSV模塊在Python中提取CSV文件的標題.CSV文件非常扁平,看起來像&#xff1a;This, That, The Other1, 2, 3我正在做以下事情&#xff1a;>讀入CSV文件并制作閱讀器對象>將讀者的迭代器推到下一行,強制它至少訪問第一行一次(來自csv模塊文檔&#xff1a;“如果在…

Spring Insight – Web應用程序分析

您是否正在使用Spring Framework編寫Web應用程序&#xff1f; 您是否曾經想過引擎蓋下發生了什么&#xff1f; 為什么您的應用程序響應如此緩慢&#xff1f; 在您仍然等待應用程序響應的同時&#xff0c;為什么窗外的蝸牛如此之快地消失在遠處&#xff1f; 您應該:)&#xff0c…

創建動態鏈接庫時設置導出函數的方法

有兩種方法1.使用模塊定義文件, 2.在要導出的函數前加上 __declspec(dllexport) 我們用VS2008新建個DLL工程&#xff0c;工程名為“TestDLL” 把默認的源文件后綴 .CPP改為.C&#xff08;C文件&#xff09; int _stdcall MyFunction(int iVariant){return 0; } 1. 使用傳統的模…

javascript的瀏覽器Bom詳解,window、location、history對象

BOM(BrowserObjectModel)也叫瀏覽器對象模型&#xff0c;描述與瀏覽器進行交互的方法和接口。BOM由多個對象組成&#xff0c; 其中代表瀏覽器窗口的Window對象是BOM的頂層對象&#xff0c;其他對象都是該對象的子對象。 JavaScript由三部分組成&#xff1a;ECMAScript,BOM&…