Java 泛型(Generic)簡介及用法詳解
引入:
看下面演示代碼測試
package cn.wen;
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo1 {
public static void main(String[] args) {
// 創(chuàng)建
ArrayList array = new ArrayList();
// 添加元素
array.add("hello");
array.add("world");
array.add("java");
//array.add(new Integer(100));
array.add(10); // JDK5以后的自動裝箱
// 等價于:array.add(Integer.valueOf(10));
// 遍歷
Iterator it = array.iterator();
while (it.hasNext()) {
// ClassCastException
String s = (String) it.next();
System.out.println(s);
}
}
}輸出異常:
hello
world
java
Exception in thread "main" java.lang.ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
at cn.wen.GenericDemo1.main(GenericDemo1.java:49)
ArrayList存儲字符串并遍歷
我們按照正常的寫法來寫這個程序, 結果確出錯了。 為什么呢?因為我們開始存儲的時候,存儲了String和Integer兩種類型的數(shù)據(jù)。 而在遍歷的時候,我們把它們都當作String類型處理的,做了轉換,所以就報錯了。
但是,它在編譯期間卻沒有告訴我們。所以,就覺得這個設計的不好。
回想一下,我們的數(shù)組
String[] strArray = new String[3];
strArray[0] = "hello";
strArray[1] = "world";
strArray[2] = 10;

集合也模仿著數(shù)組的這種做法,在創(chuàng)建對象的時候明確元素的數(shù)據(jù)類型(字符串)。這樣就不會在有問題了。
而這種技術被稱為:泛型。
泛型:是一種把類型明確的工作推遲到創(chuàng)建對象或者調(diào)用方法的時候才去明確的特殊的類型。參數(shù)化類型,把類型當作參數(shù)一樣的傳遞。
格式:
<數(shù)據(jù)類型>
此處的數(shù)據(jù)類型只能是引用類型。
好處:
A:把運行時期的問題提前到了編譯期間
B:避免了強制類型轉換
C:優(yōu)化了程序設計,解決了黃色警告線

Java 泛型
Java 泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。
假定我們有這樣一個需求:寫一個排序方法,能夠?qū)φ蛿?shù)組、字符串數(shù)組甚至其他任何類型的數(shù)組進行排序,該如何實現(xiàn)?答案是可以使用 Java 泛型。使用 Java 泛型的概念,我們可以寫一個泛型方法來對一個對象數(shù)組排序。然后,調(diào)用該泛型方法來對整型數(shù)組、浮點數(shù)數(shù)組、字符串數(shù)組等進行排序。
修改上面代碼:正常輸出
package cn.wen;
import java.util.ArrayList;
import java.util.Iterator;
public class GenericDemo1 {
public static void main(String[] args) {
// 創(chuàng)建
ArrayList<String> array = new ArrayList<String>();
// 添加元素
array.add("hello");
array.add("world");
array.add("java");
//array.add(new Integer(100));
//array.add(10); // JDK5以后的自動裝箱
// 等價于:array.add(Integer.valueOf(10));
// 遍歷
Iterator<String> it = array.iterator();
while (it.hasNext()) {
// ClassCastException
//String s = (String) it.next(); //改進如下
String s = it.next();
System.out.println(s);
}
}
}
泛型在哪些地方使用呢?
看API,如果類,接口,抽象類后面跟的有<E>就說要使用泛型。一般來說就是在集合中使用。
案例:
1)、用ArrayList存儲字符串元素,并遍歷。用泛型改進代碼
package cn.wen_02;
import java.util.ArrayList;
import java.util.Iterator;
public class ArrayListDemo {
public static void main(String[] args) {
// 用ArrayList存儲字符串元素,并遍歷。用泛型改進代碼
ArrayList<String> array = new ArrayList<String>();
array.add("hello");
array.add("world");
array.add("java");
Iterator<String> it = array.iterator();
while (it.hasNext()) {
String s = it.next();
System.out.println(s);
}
System.out.println("-----------------");
for (int x = 0; x < array.size(); x++) {
String s = array.get(x);
System.out.println(s);
}
}
}2)、需求:存儲自定義對象并遍歷。
A:創(chuàng)建學生類
B:創(chuàng)建集合對象
C:創(chuàng)建元素對象
D:把元素添加到集合
E:遍歷集合
package cn.wen_02;
import java.util.ArrayList;
import java.util.Iterator;
/*
* 需求:存儲自定義對象并遍歷。
*
* A:創(chuàng)建學生類
* B:創(chuàng)建集合對象
* C:創(chuàng)建元素對象
* D:把元素添加到集合
* E:遍歷集合
*/
public class ArrayListDemo2 {
public static void main(String[] args) {
// 創(chuàng)建集合對象
// JDK7的新特性:泛型推斷。
// ArrayList<Student> array = new ArrayList<>();
// 但是我不建議這樣使用。
ArrayList<Student> array = new ArrayList<Student>();
// 創(chuàng)建元素對象
Student s1 = new Student("小明", 40);
Student s2 = new Student("小東", 30);
Student s3 = new Student("小亮", 26);
// 添加元素
array.add(s1);
array.add(s2);
array.add(s3);
// 遍歷
Iterator<Student> it = array.iterator();
while (it.hasNext()) {
Student s = it.next();
System.out.println(s.getName() + "---" + s.getAge());
}
System.out.println("------------------");
for (int x = 0; x < array.size(); x++) {
Student s = array.get(x);
System.out.println(s.getName() + "---" + s.getAge());
}
}
}學生類:
package cn.wen_02;
public class Student {
// 姓名
private String name;
// 年齡
private int age;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}泛型由來
為什么會有泛型呢 ?
- 通過案例引入
- 早期的Object類型可以接收任意的對象類型,但是在實際的使用中,會有類型轉換的問題。也就存在這隱患,所以Java提供了泛型來解決這個安全問題。
泛型應用
- 泛型類
把泛型定義在類上
格式 :public class 類名 < 泛型類型 1,…>
注意 : 泛型類型必須是引用類型
- 泛 型方法
把泛型定義在方法上
格式 :public < 泛型類型 > 返回類型 方法名 ( 泛型類型 .)
- 泛 型接口
• 把泛型定義在接口上
• 格式 :public interface 接口名 < 泛型類型 1…>
泛型類的聲明和非泛型類的聲明類似,除了在類名后面添加了類型參數(shù)聲明部分。
和泛型方法一樣,泛型類的類型參數(shù)聲明部分也包含一個或多個類型參數(shù),參數(shù)間用逗號隔開。一個泛型參數(shù),也被稱為一個類型變量,是用于指定一個泛型類型名稱的標識符。因為他們接受一個或多個參數(shù),這些類被稱為參數(shù)化的類或參數(shù)化的類型。
泛型類:
package cn.wen_04;
/*
* 泛型類:把泛型定義在類上
*/
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
測試類
package cn.wen_04;
/*
* 泛型類的測試
*/
public class ObjectToolDemo {
public static void main(String[] args) {
// ObjectTool ot = new ObjectTool();
//
// ot.setObj(new String("風清揚"));
// String s = (String) ot.getObj();
// System.out.println("姓名是:" + s);
//
// ot.setObj(new Integer(30));
// Integer i = (Integer) ot.getObj();
// System.out.println("年齡是:" + i);
// ot.setObj(new String("林青霞"));
// // ClassCastException
// Integer ii = (Integer) ot.getObj();
// System.out.println("姓名是:" + ii);
System.out.println("-------------");
ObjectTool<String> ot = new ObjectTool<String>();
// ot.setObj(new Integer(27)); //這個時候編譯期間就過不去
ot.setObj(new String("林青霞"));
String s = ot.getObj();
System.out.println("姓名是:" + s);
ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
// ot2.setObj(new String("風清揚"));//這個時候編譯期間就過不去
ot2.setObj(new Integer(27));
Integer i = ot2.getObj();
System.out.println("年齡是:" + i);
}
}實例
如下實例演示了我們?nèi)绾味x一個泛型類:
public class Box<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box<Integer> integerBox = new Box<Integer>();
Box<String> stringBox = new Box<String>();
integerBox.add(new Integer(10));
stringBox.add(new String("HolleWorld"));
System.out.printf("整型值為 :%d\n\n", integerBox.get());
System.out.printf("字符串為 :%s\n", stringBox.get());
}
}2、泛型方法
你可以寫一個泛型方法,該方法在調(diào)用時可以接收不同類型的參數(shù)。根據(jù)傳遞給泛型方法的參數(shù)類型,編譯器適當?shù)靥幚砻恳粋€方法調(diào)用。
下面是定義泛型方法的規(guī)則:
- 所有泛型方法聲明都有一個類型參數(shù)聲明部分(由尖括號分隔),該類型參數(shù)聲明部分在方法返回類型之前(在下面例子中的<E>)。
- 每一個類型參數(shù)聲明部分包含一個或多個類型參數(shù),參數(shù)間用逗號隔開。一個泛型參數(shù),也被稱為一個類型變量,是用于指定一個泛型類型名稱的標識符。
- 類型參數(shù)能被用來聲明返回值類型,并且能作為泛型方法得到的實際參數(shù)類型的占位符。
- 泛型方法體的聲明和其他方法一樣。注意類型參數(shù)只能代表引用型類型,不能是原始類型(像int,double,char的等)。
泛型方法類:
package cn.itcast_05;
//未使用泛型之前
//public class ObjectTool<T> {
// // public void show(String s) {
// // System.out.println(s);
// // }
// //
// // public void show(Integer i) {
// // System.out.println(i);
// // }
// //
// // public void show(Boolean b) {
// // System.out.println(b);
// // }
//
// public void show(T t) {
// System.out.println(t);
// }
// }
/*
* 泛型方法:把泛型定義在方法上
*/
public class ObjectTool {
public <T> void show(T t) {
System.out.println(t);
}
}測試泛型方法類:
package cn.wen_05;
public class ObjectToolDemo {
public static void main(String[] args) {
// ObjectTool ot = new ObjectTool();
// ot.show("hello");
// ot.show(100);
// ot.show(true);
// ObjectTool<String> ot = new ObjectTool<String>();
// ot.show("hello");
//
// ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
// ot2.show(100);
//
// ObjectTool<Boolean> ot3 = new ObjectTool<Boolean>();
// ot3.show(true);
// 說明泛型類是沒有問題的
// 但是呢,誰說了我的方法一定要和類的類型的一致呢?
// 要是類上沒有泛型的話,方法還能不能接收任意類型的參數(shù)了呢?
// 定義泛型方法后
ObjectTool ot = new ObjectTool();
ot.show("hello");
ot.show(100);
ot.show(true);
}
}實例
下面的例子演示了"extends"如何使用在一般意義上的意思"extends"(類)或者"implements"(接口)。該例子中的泛型方法返回三個可比較對象的最大值。
public class MaximumTest
{
// 比較三個值并返回最大值
public static <T extends Comparable<T>> T maximum(T x, T y, T z)
{
T max = x; // 假設x是初始最大值
if ( y.compareTo( max ) > 0 ){
max = y; //y 更大
}
if ( z.compareTo( max ) > 0 ){
max = z; // 現(xiàn)在 z 更大
}
return max; // 返回最大對象
}
public static void main( String args[] )
{
System.out.printf( "%d, %d 和 %d 中最大的數(shù)為 %d\n\n",
3, 4, 5, maximum( 3, 4, 5 ) );
System.out.printf( "%.1f, %.1f 和 %.1f 中最大的數(shù)為 %.1f\n\n",
6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
System.out.printf( "%s, %s 和 %s 中最大的數(shù)為 %s\n","pear",
"apple", "orange", maximum( "pear", "apple", "orange" ) );
}
}3, 4 和 5 中最大的數(shù)為 5 6.6, 8.8 和 7.7 中最大的數(shù)為 8.8 pear, apple 和 orange 中最大的數(shù)為 pear
3、泛型接口
接口類:
package cn.wen_06;
/*
* 泛型接口:把泛型定義在接口上
*/
public interface Inter<T> {
public abstract void show(T t);
}實現(xiàn)類:
package cn.wen_06;
//實現(xiàn)類在實現(xiàn)接口的時候
//第一種情況:已經(jīng)知道該是什么類型的了
//public class InterImpl implements Inter<String> {
//
// @Override
// public void show(String t) {
// System.out.println(t);
// }
// }
//第二種情況:還不知道是什么類型的
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}測試類:
package cn.wen_06;
public class InterDemo {
public static void main(String[] args) {
// 第一種情況的測試
// Inter<String> i = new InterImpl();
// i.show("hello");
// // 第二種情況的測試
Inter<String> i = new InterImpl<String>();
i.show("hello");
Inter<Integer> ii = new InterImpl<Integer>();
ii.show(100);
}
}泛型高級(通配符)
- 泛型通配符<?>
任意類型,如果沒有明確,那么就是 Object 以及任意的 Java 類了
- ? extends E
向下限定, E 及其子類
- ? super E
向上限定, E 及其父類
package cn.wen;
import java.util.ArrayList;
import java.util.Collection;
/*
* 泛型高級(通配符)
* ?:任意類型,如果沒有明確,那么就是Object以及任意的Java類了
* ? extends E:向下限定,E及其子類
* ? super E:向上限定,E極其父類
*/
public class GenericDemo {
public static void main(String[] args) {
// 泛型如果明確的寫的時候,前后必須一致
Collection<Object> c1 = new ArrayList<Object>();
// Collection<Object> c2 = new ArrayList<Animal>();
// Collection<Object> c3 = new ArrayList<Dog>();
// Collection<Object> c4 = new ArrayList<Cat>();
// ?表示任意的類型都是可以的
Collection<?> c5 = new ArrayList<Object>();
Collection<?> c6 = new ArrayList<Animal>();
Collection<?> c7 = new ArrayList<Dog>();
Collection<?> c8 = new ArrayList<Cat>();
// ? extends E:向下限定,E及其子類
// Collection<? extends Animal> c9 = new ArrayList<Object>();//報錯
Collection<? extends Animal> c10 = new ArrayList<Animal>();
Collection<? extends Animal> c11 = new ArrayList<Dog>();
Collection<? extends Animal> c12 = new ArrayList<Cat>();
// ? super E:向上限定,E極其父類
Collection<? super Animal> c13 = new ArrayList<Object>();
Collection<? super Animal> c14 = new ArrayList<Animal>();
// Collection<? super Animal> c15 = new ArrayList<Dog>();
// Collection<? super Animal> c16 = new ArrayList<Cat>();
}
}
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}到此這篇關于Java 泛型(Generic)概述及使用的文章就介紹到這了,更多相關Java 泛型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java異常報錯:java.nio.file.FileSystemException的多種解決方案
在Java應用程序中處理文件和目錄時,java.nio.file.FileSystemException是一個常見的異常,這個異常發(fā)生在嘗試進行文件系統(tǒng)操作時,本文將詳細探討FileSystemException的成因,并提供多種解決方案,需要的朋友可以參考下2024-12-12
利用feign調(diào)用返回object類型轉換成實體
這篇文章主要介紹了利用feign調(diào)用返回object類型轉換成實體,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
@CacheEvict + redis實現(xiàn)批量刪除緩存
這篇文章主要介紹了@CacheEvict + redis實現(xiàn)批量刪除緩存方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10
解決java.util.NoSuchElementException異常正確方法
java.util.NoSuchElementException是Java中的一種異常,表示在迭代器或枚舉中找不到元素,這篇文章主要給大家介紹了關于解決java.util.NoSuchElementException異常的相關資料,需要的朋友可以參考下2023-11-11
Java調(diào)用打印機的2種方式舉例(無驅(qū)/有驅(qū))
我們平時使用某些軟件或者在超市購物的時候都會發(fā)現(xiàn)可以使用打印機進行打印,這篇文章主要給大家介紹了關于Java調(diào)用打印機的2種方式,分別是無驅(qū)/有驅(qū)的相關資料,需要的朋友可以參考下2023-11-11

