java基礎-泛型舉例詳解

泛型

  泛型是JDK5.0增加的新特性,泛型的本質是參數化類型,即所操作的數據類型被指定為一個參數。這種類型參數可以在類、接口、和方法的創建中,分別被稱為泛型類、泛型接口、泛型方法。

一、認識泛型

  在沒有泛型之前,通過對類型Object的引用來實現參數的"任意化",但"任意化"帶來的缺點是需要顯示的強制類型轉換,此種轉換要求開發者對實際參數類型預知的情況下進行,對于強制轉換錯誤的情況,編譯器可能不會提示錯誤,但在運行時會出現異常,這是一個安全隱患。

  舉例:不使用泛型實現參數化類型

 1 package generic;
 2 
 3 public class NoGeneric {
 4     private Object ob;    //定義通用類型成員
 5     public NoGeneric(Object ob) {
 6         this.ob = ob;
 7     }
 8     public Object getOb() {
 9         return ob;
10     }
11     public void setOb(Object ob) {
12         this.ob = ob;
13     }
14     public void showType() {
15         System.out.println("實際類型是:"+ob.getClass().getName());
16     }
17 }
18 
19 package generic;
20 
21 public class NoGenericDemo {
22 
23     public static void main(String[] args) {
24         // TODO 自動生成的方法存根
25         //定義類NoGener的一個Integer版本
26         NoGeneric intob = new NoGeneric(new Integer(66));
27         intob.showType();
28         int i = (Integer)intob.getOb();
29         System.out.println("value="+ i);
30         System.out.println("-----------------------------");
31         //定義類NoGeneric的一個String版本
32         NoGeneric strob = new NoGeneric(new String("hello"));
33         strob.showType();
34         String s = (String)strob.getOb();
35         System.out.println("value="+ s);
36     }
37 }

?  執行結果為:

實際類型是:java.lang.Integer
value=66
-----------------------------
實際類型是:java.lang.String
value=hello

?

?

?  上面的實例有兩點需要注意:首先如下語句

String s = (String)strob.getOb();

?

  在使用時必須明確指定返回對象需要被強制轉化的類型為String,否則無法編譯通過;其次,由于intob和strob都屬于NoGeneric的類型,假如執行如下語句

intob = strob;

?

  此種賦值,語法上是合法的,而在語義上是錯誤的,對于這種情況,只有在運行時才會出現異常,使用泛型就不會出現上述錯誤,泛型的好處就是在編譯期 檢查類型,捕捉類型不匹配錯誤,并且所有強制轉換都是自動和隱式的,提高代碼的重用率.

  舉例 2:使用泛型使用泛型實現參數實例化類型

package generic;public class Generic<T> {private T ob; //定義泛型成員變量 public Generic(T ob) {this.ob = ob;}public T getOb() {return ob;}public void setOb(T ob) {this.ob = ob;}public void showType() {System.out.println("實例類型為:" + ob.getClass().getName());}
}package generic;public class GenericDemo {public static void main(String[] args) {// TODO 自動生成的方法存根//定義泛型Generic的一個Integer的版本Generic<Integer> intob = new Generic<Integer>(88);intob.showType();int i = intob.getOb();System.out.println("value=" + i);System.out.println("----------------------");//定義泛型Generic的一個String版本Generic<String> strob = new Generic<String>("hello");strob.showType();String s = strob.getOb();System.out.println("value=" + s);}
}

  運行結果為:

實例類型為:java.lang.Integer
value=88
----------------------
實例類型為:java.lang.String
value=hello

?

  在引入泛型的前提下,如果再次執行

intob = strob;

?

  將提示錯誤,編譯無法通過

二、泛型定義

  泛型的語法可歸納為

class class-name <type-param-list>{//......}

?

  實例化泛型的語法為:

class-name <type-param-list> obj =  new class-name<type-param-list>(cons-arg-list);

?

  type-param-list用于指明當前泛型類可接受的類型參數占位符的個數; ? 如:

class Generic<T>{//......}

?

  這里的T是類型參數的名稱,并且只允許傳一個類型參數給Generic類,在創建對象時,T用作傳遞 給Generic的實際類型的占位符,每當聲明類型參數時,只需用目標類型替換T即可. ? 如:

Generic <Integer> intob; 

?

  聲明對象時占位符T用于指定實際類型,如果傳遞實際類型為Integer,屬性ob就是Integer類型,類型T還可以指定方法的返回類型 ? ? 如:

public T getOb(){return ob;
}

?  理解泛型有三點需要注意:

    1、泛型的類型參數只能為類類型(包括自定義類),不能是基本數據類型。

    2、同一種泛型可以對應多個版本(因為類型參數時不確定的)、不同版本的泛型類實例是不兼容的。

    3、泛型的類型參數可以有多個。

    注意 ? 根據慣例,泛型類定義時通常使用一個唯一的大寫字母表示一個類型參數.

三、有界類型

?  定義泛型類時,可以向類型參數指定任何類型信息,特別是集合框架操作中,可以最大限度地提高適用范圍,但有時候需要對類型參數的取值進行一定程度的限制,以使數據具有可操作性.

   為了處理這種情況,java提供了有界類型,.在指定類型參數時可以使用extends關鍵字限制此類型參數代表的類必須是繼承自指定父類或父類本身.

  使用extends關鍵字實現有界類型泛型類的定義

package generic;public class BoundGeneric<T extends Number> {//定義泛型數組
    T[] array;public BoundGeneric(T[] array) {this.array = array;}//計算總和public double sum() {double sum = 0.0;for(T t : array) {sum = sum + t.doubleValue();}return sum;}
}

?

  BoundGeneric類的定義中,使用extends將T的類型限制為Number類及其子類,故可以再定義過程中調用Number類的doubleValue方法,現在分別指定Integer,double,String類型作為類型參數,測試BoundGeneric:

package generic;public class BoundGenericDemo {public static void main(String[] args) {// TODO 自動生成的方法存根//使用整形數組構造泛型對象Integer[] intArray = {1, 2, 3, 4};BoundGeneric<Integer> iobj = new BoundGeneric<Integer>(intArray);System.out.println("iobj的和為:" + iobj.sum());//使用Double型數組構造泛型對象Double[] douArray = {1.2, 2.3, 3.4, 4.5};BoundGeneric<Double> dobj = new BoundGeneric<Double>(douArray);System.out.println("dobj的和為:" + dobj.sum());String[] strArray = {"str1","str2"};//下面的語句將會報錯,String不是Number的子類//BoundGeneric<String> sobj = new BoundGeneric<String>(strArray);
    }
}

?

  運行結果為:

iobj的和為:10.0
dobj的和為:11.4

  注:在使用extends(如:T extends someClass)聲明的泛型類進行實例化時允許傳遞的參數類型為:如果someClass是類,可以傳遞someClass本身及其子類;如果someClass接口可以傳遞實現接口的類

四、通配符

  首先在說通配符之前先看一下這段代碼:使用前面定義的Generic類.

package generic;public class WildcarDemo {public static void func(Generic <Object> g) {//...
    }public static void main(String args[]) {Generic <Object> obj = new Generic<Object>(12);func(obj);Generic<Integer> iobj = new Generic<Integer>(12);//這里講產生一個錯誤:類型 WildcarDemo 中的方法 func(Generic<Object>)對于參數(Generic<Integer>)不適用//func(iobj);
    }
}

?

  上述代碼的func()方法的創建意圖是能夠處理各種類型參數的Generic對象,因為Generic是泛型,所以在使用時需要為其指定具體的參數化類型Object,看似不成問題,

  但在

func(iobj);

?

  處產生一個編譯錯誤,因為func定義過程中以明確聲明的Generic的類型參數為Object,這里試圖將Generic<Integer>類型的對象傳遞給func()方法,類型不匹配導致編譯錯誤.這種情況可以使用通配符解決.通配符由"?"來表示,它代表一個未知類型

package generic;public class WildcarDemo2 {public static void func(Generic <?> g) {//...
    }public static void main(String args[]) {Generic<Object> obj = new Generic<Object>(12);func(obj);Generic<Integer> iobj = new Generic<Integer>(12);func(iobj);}
}

?

  上述代碼,在采用了通配符后語句將無誤的編譯,運行.

  在通配符使用的過程中,也可通過extends關鍵字限定通配符的界定的類型參數的范圍.

package generic;public class WildcarDemo3 {public static void func(Generic <? extends Number> g) {//...
    }public static void main(String args[]) {Generic<Object> obj = new Generic<Object>(12);//這里將產生一個錯誤:類型 WildcarDemo3 中的方法 func(Generic<? extends Number>)對于參數(Generic<Object>)不適用//func(obj);
        Generic<Integer> iobj = new Generic<Integer>(12);func(iobj);}
}

?

五、泛型的局限性

  java并沒有真正實現泛型,是編譯器在編譯的時候在字節碼上做了手腳(稱為擦除). 這種實現理念在成java泛型本身有很多漏洞, 為了避免這些問題java對泛型的使用上做了一些約束,但不可避免的還是有一些問題存在.多數的限制都是由類型擦除引起的.

  1、泛型類型不能被實例化

public class Gen<T>{T ob;public Gen(){ob = new T();}    
}

?

  Gen<T>構造器是非法的,類型擦除將變量T替換成Object,但這段代碼的本意肯定不是調用new Object().類似:如

public <T> T[]build (T[] a){T [] array = new T[2];  
  //... }

?

  類型擦除會讓這個方法總是構造一個Object[2]數組,但是可以通過調用Class.newInstance和Array.newInstance方法,利用反射構造泛型對象和數組

2、數組

  不能實例化數組如:

T[] vals;
vals = new T[10];

?

  因為T在運行時時不存在的,編譯器無法知道實際創建那種類型的數據.

  其次,不能創建一個類型特定的泛型引用的數組 ? ?如:

Gen<String> []arrays = new Gen<String>[100];

?

  上面的代碼會損害類型安全

  如果使用通配符,就可以創建泛型類型的引用數組

Gen<?> []arrays = new Gen<?>[10];

?

3、怒能用類型參數替換基本類型

  因為擦除類型后原先的類型參數被Object或者限定類型替換,而基本類型是不能被對象所存儲的,可以使用基本類型的包裝類來解決此問題

4、異常

  不能拋出也不能捕獲泛型類的異常對象,使用泛型類來擴展Throwable也是非法的. 如:

public class GenericException <T> extends Exception{//泛型類無法繼承Throwable
}

?

  不能再catch子句中使用類型參數,例如下面的方法將不能編譯

    public static <T extends Throwable> void doWork(Class<T> t) {try {//...}catch(Throwable realCause) {//...}

?

  但是在異常聲明時可以使用類型參數,如:

    public static <T extends Throwable> void doWork(T t) throws T {try {//...}catch(Throwable realCause) {throw t;}}

?

5、靜態成員

  不能在靜態變量或者靜態方法中引用類型參數 ?如:

public class Gen<T>{static T ob;static T getOb() {return ob;}
}

?這些均參考自“Java SE程序設計”,算是做個筆記,以后忘了可以翻閱一下,寫在自己的隨筆中,也希望可以幫助更多的人。如有侵權,請聯系本人刪除

?

轉載于:https://www.cnblogs.com/Mykebai/p/9123749.html

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

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

相關文章

MySQL數據庫視圖(view),視圖定義、創建視圖、修改視圖

原文鏈接&#xff1a;https://blog.csdn.net/moxigandashu/article/details/63254901轉載于:https://www.cnblogs.com/chrdai/p/9131881.html

[pytorch、學習] - 3.10 多重感知機的簡潔實現

參考 3.10. 多重感知機的簡潔實現 import torch from torch import nn from torch.nn import init import numpy as np import sys sys.path.append("..") import d2lzh_pytorch as d2l3.10.1. 定義模型 num_inputs, num_outputs, num_hiddens 784, 10, 256 # 參…

【匯編語言】——第三章課后總結

第三章 的書本上主要有以下幾個內容&#xff1a; 1.內存中字的存儲 字單元&#xff1a;即存放一個字型數據&#xff08;16位&#xff09;的內存單元&#xff0c;由兩個地址連續的內存單元組成。 小端法&#xff1a;高地址內存單元中存放字型數據的高位字節&#xff0c;低地址內…

如何從 Android 手機免費恢復已刪除的通話記錄/歷史記錄?

有一個有合作意向的人給我打電話&#xff0c;但我沒有接聽。更糟糕的是&#xff0c;我錯誤地將其刪除&#xff0c;認為這是一個騷擾電話。那么有沒有辦法從 Android 手機恢復已刪除的通話記錄呢&#xff1f;” 塞繆爾問道。如何在 Android 上恢復已刪除的通話記錄&#xff1f;如…

springBoot 登錄攔截器

1、首選創建一個繼承HandlerInterceptor的攔截器 import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /*** 攔…

[pytorch、學習] - 3.11 模型選擇、欠擬合和過擬合

參考 3.11 模型選擇、欠擬合和過擬合 3.11.1 訓練誤差和泛化誤差 在解釋上述現象之前&#xff0c;我們需要區分訓練誤差&#xff08;training error&#xff09;和泛化誤差&#xff08;generalization error&#xff09;。通俗來講&#xff0c;前者指模型在訓練數據集上表現…

關于'java' 不是內部或外部命令,也不是可運行的程序 或批處理文件 和 錯誤: 找不到或無法加載主類 helloworld的問題...

一、前幾天電腦重裝了一次系統將java配置的環境變量都弄沒了&#xff0c;自己添加了兩個新的變量JAVA_HOME&#xff08;自己jdk的地址&#xff09;以及在path中添加%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; 然后因為這幾天都是用eclipse進行編程的&#xff0c;沒有出現問題&#…

spring-boot注解詳解(一)

spring-boot注解詳解(一) SpringBootApplication SpringBootApplication (默認屬性)Configuration EnableAutoConfiguration ComponentScan。 Configuration&#xff1a;提到Configuration就要提到他的搭檔Bean。使用這兩個注解就可以創建一個簡單的spring配置類&#xf…

前端基礎-jQuery的優點以及用法

一、jQuery介紹 jQuery是一個輕量級的、兼容多瀏覽器的JavaScript庫。jQuery使用戶能夠更方便地處理HTML Document、Events、實現動畫效果、方便地進行Ajax交互&#xff0c;能夠極大地簡化JavaScript編程。它的宗旨就是&#xff1a;“Write less, do more.“二、jQuery的優勢 一…

[pytorch、學習] - 3.12 權重衰減

參考 3.12 權重衰減 本節介紹應對過擬合的常用方法 3.12.1 方法 正則化通過為模型損失函數添加懲罰項使學出的模型參數更小,是應對過擬合的常用手段。 3.12.2 高維線性回歸實驗 import torch import torch.nn as nn import numpy as np import sys sys.path.append("…

Scapy之ARP詢問

引言 校園網中&#xff0c;有同學遭受永恒之藍攻擊&#xff0c;但是被殺毒軟件查下&#xff0c;并知道了攻擊者的ip也是校園網。所以我想看一下&#xff0c;這個ip是PC&#xff0c;還是路由器。 在ip視角&#xff0c;路由器和pc沒什么差別。 實現 首先是構造arp報文&#xff0c…

spring-boot注解詳解(二)

ResponseBody 作用&#xff1a; 該注解用于將Controller的方法返回的對象&#xff0c;通過適當的HttpMessageConverter轉換為指定格式后&#xff0c;寫入到Response對象的body數據區。使用時機&#xff1a; 返回的數據不是html標簽的頁面&#xff0c;而是其他某種格式的數據時…

轉:org.apache.maven.archiver.MavenArchiver.getManifest錯誤

eclipse導入新的maven項目時&#xff0c;pom.xml第一行報錯&#xff1a; org.apache.maven.archiver.MavenArchiver.getManifest(org.apache.maven.project.MavenProject, org.apache.maven.archiver.MavenArchiveConfiguration) 解決辦法&#xff1a; 1、Help——>Install …

Codeforces Round #524 Div. 2 翻車記

A&#xff1a;簽到。room里有一個用for寫的&#xff0c;hack了一發1e8 1&#xff0c;結果用了大概600ms跑過去了。慘絕人寰。 #include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorith…

[pytorch、學習] - 3.13 丟棄法

參考 3.13 丟棄法 過擬合問題的另一種解決辦法是丟棄法。當對隱藏層使用丟棄法時,隱藏單元有一定概率被丟棄。 3.12.1 方法 3.13.2 從零開始實現 import torch import torch.nn as nn import numpy as np import sys sys.path.append("..") import d2lzh_pytorc…

springboot---request 中Parameter,Attribute區別

HttpServletRequest類既有getAttribute()方法&#xff0c;也由getParameter()方法&#xff0c;這兩個方法有以下區別&#xff1a; &#xff08;1&#xff09;HttpServletRequest類有setAttribute()方法&#xff0c;而沒有setParameter()方法 &#xff08;2&#xff09;當兩個…

Python之令人心煩意亂的字符編碼與轉碼

ASC-II碼&#xff1a;英文1個字節&#xff08;8 byte&#xff09;&#xff0c;不支持中文&#xff1b; 高大上的中國&#xff0c;擴展出自己的gbk、gb2312、gb2318等字符編碼。 由于各個國家都有自己的編碼&#xff0c;于是就需要統一的編碼形式用于國際流傳&#xff0c;防止亂…

[pytorch、學習] - 4.1 模型構造

參考 4.1 模型構造 讓我們回顧以下多重感知機的簡潔實現中包含單隱藏層的多重感知機的實現方法。我們首先構造Sequential實例,然后依次添加兩個全連接層。其中第一層的輸出大小為256,即隱藏層單元個數是256;第二層的輸出大小為10,即輸出層單元個數是10. 4.1.1 繼承Module類來…

springboot---基本模塊詳解

概述 1.基于Spring框架的“約定優先于配置&#xff08;COC&#xff09;”理念以及最佳實踐之路。 2.針對日常企業應用研發各種場景的Spring-boot-starter自動配置依賴模塊&#xff0c;且“開箱即用”&#xff08;約定spring-boot-starter- 作為命名前綴&#xff0c;都位于org.…

第二課 運算符(day10)

第二課 運算符(day10) 一、運算符 結果是值 算數運算 a 10 * 10 賦值運算 a a 1 a1 結果是布爾值 比較運算 a 1 > 5 邏輯運算 a 1>6 or 11 成員運算 a "蚊" in "鄭建文" 二、基本數據類型 1、數值…