仿制药名言_仿制药的美丽与陌生
仿制藥名言
最近,我正在為Oracle認證專家Java SE 7程序員考試做準備,而我恰巧在Java泛型領域遇到了一些看起來很奇怪的構造。 但是,我也看到了一些巧妙而優雅的代碼。 我發現這些示例值得分享,這不僅是因為它們可以使您的設計選擇更加容易,并且使代碼更加健壯和可重用,而且還因為其中的某些示例在您不習慣泛型時非常棘手。 我決定將這篇文章分為四個章節,這些章節幾乎反映了我在學習和工作經歷中對仿制藥的經驗。
您了解泛型嗎?
當我們四處看看時,可以發現泛型在Java Universe的許多不同框架中都大量使用。 它們從Web應用程序框架到Java本身的集合。 由于這個話題已經在我之前被很多人解釋過了,所以我將只列出我認為有價值的資源,然后轉到有時根本沒有提及或解釋得不好的東西(通常在網上發布的注釋或文章中) 。 因此,如果您不了解核心泛型概念,則可以查看以下一些材料:
- Katherine Sierra和Bert Bates的SCJP Sun認證Java 6考試程序員
- 對我而言,本書的主要目的是為參加Oracle提供的OCP考試做好準備。
- 課程: Oracle的泛型(已更新)
- 資源由Oracle本身提供。
- Maurice Naftalin和Philip Wadler的Java泛型和集合
- O'Reilly Media制作的另一本很棒的Java書籍。
泛型不允許做什么?
假設您了解泛型,并希望了解更多信息,那么請轉到無法完成的工作。 令人驚訝的是,有很多東西無法與泛型一起使用。 在使用泛型時,我選擇了以下六個避免陷阱的示例。
類型為<T>靜態字段
許多沒有經驗的程序員常犯的一個常見錯誤是試圖聲明靜態成員。 在下面的示例中可以看到,任何嘗試都會導致編譯器錯誤,如下所示: Cannot make a static reference to the non-static type T 。
public class StaticMember<T> {// causes compiler errorstatic T member; }類型為<T>實例
另一個錯誤是嘗試通過在泛型類型上調用new來實例化任何類型。 這樣,編譯器將導致錯誤提示: Cannot instantiate the type T
public class GenericInstance<T> {public GenericInstance() {// causes compiler errornew T();} }與原始類型不兼容
使用泛型時最大的限制之一似乎是它們與原始類型不兼容。 的確,您不能在聲明中直接使用基元,但是,可以用適當的包裝器類型替換基元,然后就可以了。 整個情況在以下示例中呈現:
public class Primitives<T> {public final List<T> list = new ArrayList<>();public static void main(String[] args) {final int i = 1;// causes compiler error// final Primitives<int> prim = new Primitives<>();final Primitives<Integer> prim = new Primitives<>();prim.list.add(i);} }在編譯期間, Primitives類的第一個實例化將失敗,并出現與以下錯誤類似的錯誤: Syntax error on token "int", Dimensions expected after this token 。 使用包裝器類型和少量自動裝箱魔術可繞過此限制。
<T>類型的數組
使用泛型的另一個明顯限制是無法實例化泛型類型的數組。 考慮到數組對象的基本特征,原因很明顯–它們在運行時保留其類型信息。 如果違反了它們的運行時類型完整性,則運行時異常ArrayStoreException可以挽救這一天。
public class GenericArray<T> {// this one is finepublic T[] notYetInstantiatedArray;// causes compiler errorpublic T[] array = new T[5]; }但是,如果嘗試直接實例化一個通用數組,則將出現如下編譯錯誤: Cannot create a generic array of T 。
通用異常類
有時,程序員可能需要傳遞泛型類型的實例以及引發異常。 這在Java中是不可能的。 下面的示例描述了這種努力。
// causes compiler error public class GenericException<T> extends Exception {}當您嘗試創建這樣的異常時,您將得到如下消息: The generic class GenericException<T> may not subclass java.lang.Throwable 。
關鍵字super和extends替代含義
值得一提的最后一個限制,特別是對于新手來說,是泛型時關鍵字super和extends的替代含義。 為了生成利用泛型的精心設計的代碼,了解這一點非常有用。
- <? extends T>
- 含義:通配符是指擴展類型T和類型T本身的任何類型。
- <? super T>
- 含義:通配符是指T的任何超類型以及T本身。
一點點的美
關于Java我最喜歡的事情之一是它的強類型。 眾所周知,泛型是在Java 5中引入的,它們被用來使我們更容易使用集合(它們不僅在集合中被用于更多領域,但這是設計階段泛型的核心論點之一) 。 即使泛型僅提供編譯時保護,并且不輸入字節碼,但它們提供了相當有效的方式來確保類型安全。 以下示例顯示了泛型的一些不錯的功能或用例。
泛型適用于類和接口
這可能一點都不令人驚訝,但是是的-接口和泛型是兼容的構造。 盡管將泛型與接口結合使用非常普遍,但我發現這一事實實際上是非常酷的功能。 這使程序員可以在考慮類型安全和代碼重用的情況下創建甚至更高效的代碼。 例如,考慮以下來自包java.lang接口Comparable示例:
public interface Comparable<T> {public int compareTo(T o); }泛型的簡單介紹使得有可能從compareTo方法中省略check實例,從而使代碼更具凝聚力并提高了可讀性。 通常,泛型有助于使代碼更易于閱讀和理解,并有助于引入類型順序。
泛型允許優雅使用范圍
關于限制通配符,有一個很好的例子說明了在庫類Collections可以實現的目標。 此類聲明方法copy ,該方法在以下示例中定義,并使用有界通配符來確保列表的復制操作的類型安全。
public static <T> void copy(List<? super T> dest, List<? extends T> src) { ... }讓我們仔細看看。 方法copy被聲明為返回void的靜態泛型方法。 它接受兩個參數-目標和源(并且兩者都是有界的)。 目標必須存儲僅屬于T或T類型本身的超類型的類型。 另一方面,源必定僅由T類型的擴展類型或T類型本身構成。 這兩個約束保證了集合和復制操作都保持類型安全。 我們不需要使用數組,因為它們通過拋出上述ArrayStoreException異常防止了任何類型安全沖突。
泛型支持多邊界
不難想象為什么人們會只使用一種簡單的邊界條件而不是使用更多的條件。 實際上,這樣做很容易。 考慮以下示例:我需要創建一個接受參數的方法,該參數既是Comparable也是數字List 。 開發人員將被迫創建不必要的接口ComparableList,以便在通用時間內履行上述合同。
public class BoundsTest {interface ComparableList extends List, Comparable {}class MyList implements ComparableList { ... }public static void doStuff(final ComparableList comparableList) {}public static void main(final String[] args) {BoundsTest.doStuff(new BoundsTest().new MyList());} }在執行此任務之后,我們可以忽略這些限制。 使用泛型可以使我們創建滿足所需合同的具體類,但使doStuff方法盡可能開放。 我發現的唯一缺點是語法相當冗長。 但是,由于它仍然保持很好的可讀性和易于理解性,因此我可以忽略此缺陷。
public class BoundsTest {class MyList<T> implements List<T>, Comparable<T> { ... }public static <T, U extends List<T> & Comparable<T>> void doStuff(final U comparableList) {}public static void main(final String[] args) {BoundsTest.doStuff(new BoundsTest().new MyList<String>());} }有點奇怪
我決定為這篇文章的最后一章專門介紹到目前為止我遇到的兩種最奇怪的構造或行為。 您極有可能永遠不會遇到這樣的代碼,但是我發現提到它很有趣。 因此,事不宜遲,讓我們見識這些奇怪的東西。
尷尬的代碼
與任何其他語言構造一樣,您可能最終會遇到一些看起來很奇怪的代碼。 我想知道最奇怪的代碼是什么樣的,以及它是否甚至可以通過編譯。 我能想到的最好的是下面的代碼。 您可以猜測該代碼是否可以編譯?
public class AwkwardCode<T> {public static <T> T T(T T) {return T;} }即使這是一個非常糟糕的編碼示例,也可以成功編譯,并且應用程序可以正常運行。 第一行聲明泛型類AwkwardCode ,第二行聲明泛型方法T 方法T是返回T實例的通用方法。 它采用T類型的參數,不幸的是稱為T 該參數也會在方法主體中返回。
通用方法調用
最后一個示例顯示了與泛型結合使用時類型推斷的工作原理。 當我看到一段代碼不包含方法調用的通用簽名卻聲稱要通過編譯時,我偶然發現了這個問題。 當某人對泛型只有很少的經驗時,這樣的代碼可能一見鐘情。 您能否解釋以下代碼的行為?
public class GenericMethodInvocation {public static void main(final String[] args) {// 1. returns trueSystem.out.println(Compare.<String> genericCompare("1", "1"));// 2. compilation errorSystem.out.println(Compare.<String> genericCompare("1", new Long(1)));// 3. returns falseSystem.out.println(Compare.genericCompare("1", new Long(1)));} }class Compare {public static <T> boolean genericCompare(final T object1, final T object2) {System.out.println("Inside generic");return object1.equals(object2);} }好吧,讓我們分解一下。 第一次調用genericCompare非常簡單。 我表示參數類型將是什么類型的方法,并提供該類型的兩個對象-此處沒有奧秘。 由于Long不是String對genericCompare第二次調用無法編譯。 最后,對genericCompare第三次調用返回false 。 這很奇怪,因為該方法被聲明為接受相同類型的兩個參數,但是最好將其傳遞給String文字和Long對象。 這是由編譯期間執行的類型擦除過程引起的。 由于方法調用未使用泛型的<String>語法,因此編譯器無法告訴您要傳遞兩種不同的類型。 始終記住,最接近的共享繼承類型用于查找匹配的方法聲明。 意思是,當genericCompare接受object1和object2 ,它們被genericCompare轉換為Object ,但由于運行時多態性而被比較為String和Long實例–因此該方法返回false 。 現在讓我們修改一下代碼。
public class GenericMethodInvocation {public static void main(final String[] args) {// 1. returns trueSystem.out.println(Compare.<String> genericCompare("1", "1"));// 2. compilation errorSystem.out.println(Compare.<String> genericCompare("1", new Long(1)));// 3. returns falseSystem.out.println(Compare.genericCompare("1", new Long(1)));// compilation errorCompare.<? extends Number> randomMethod();// runs fineCompare.<Number> randomMethod();} }class Compare {public static <T> boolean genericCompare(final T object1, final T object2) {System.out.println("Inside generic");return object1.equals(object2);}public static boolean genericCompare(final String object1, final Long object2) {System.out.println("Inside non-generic");return object1.equals(object2);}public static void randomMethod() {} }此新代碼示例通過添加genericCompare方法的非泛型版本并定義一個不執行任何操作并從GenericMethodInvocation類的main方法調用兩次的新randomMethod來修改Compare類。 由于我提供了與給定調用匹配的新方法,因此該代碼使對genericCompare的第二次調用genericCompare可能。 但這引發了一個關于另一個奇怪行為的問題–第二個呼叫是否通用? 事實證明–不,不是。 但是,仍然可以使用泛型的<String>語法。 為了更清楚地展示此功能,我使用此通用語法創建了對randomMethod新調用。 再次由于類型擦除過程而實現了這一點-擦除了這種通用語法。
但是,當有界通配符出現在舞臺上時,這種情況會改變。 編譯器以編譯器錯誤的形式向我們發送清晰的消息,說: Wildcard is not allowed at this location ,這使得無法編譯代碼。 要編譯和運行代碼,必須注釋掉第12行。以這種方式修改代碼后,它將產生以下輸出:
Inside generic true Inside non-generic false Inside non-generic false翻譯自: https://www.javacodegeeks.com/2014/06/beauty-and-strangeness-of-generics.html
仿制藥名言
總結
以上是生活随笔為你收集整理的仿制药名言_仿制药的美丽与陌生的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java中的代理设计模式
- 下一篇: ps除污快捷键(ps去污工具快捷键)