泛型
泛型(Generic)是Java編程語言的強大功能。
泛型的本質是參數化類型,在不創建新的類型的情況下,通過泛型指定某一個類、方法或接口成不同類型。將類型由原來的具體類參數化,此時的參數可以稱爲類型形參
public class Test1 {
/**
* @param args
* ArrayList<Integer>,ArrayList被指定了一個Integer的類型形參,所以此時的ArrayList只能添加Integer的對象
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
System.out.println(list);
}
}
泛型的優點
1.在編譯時進行更強的類型檢查。Java編譯器將強類型檢查應用於通用代碼,如果代碼違反類型安
全,則會發出錯誤。修復編譯時錯誤比修復運行時錯誤容易,後者可能很難找到。
2.消除類型轉換
3.可以幫助我們寫出更加通用的代碼
泛型擦除
java中的泛型 只是在編譯期做語法檢查 對你進行限制
泛型只在編譯期有效(class).運行期沒有泛型,泛型就被擦除了,所有類型都變成了相同的基本類型
public class Test1 {
/**
* @param args
* ArrayList<Integer>的一個對象,在這個集合中添加一個字符串數據,如何實現呢?
* 泛型只在編譯期有效,在運行期會被擦除掉
* @throws Exception
*/
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList<>();
list.add(111);
list.add(222);
Class clazz = Class.forName("java.util.ArrayList"); //獲取字節碼對象
Method m = clazz.getMethod("add", Object.class); //獲取add方法,add方法中可以放任意對象所以參數是Object.class
m.invoke(list, "abc");
System.out.println(list);
}
}
泛型類
泛型類型修飾類爲泛型類,例如List
1.泛型類可以由多個泛型參數例如Class<T,R>
2.泛型參數如果想使用基本數據類型的話必須傳遞其包裝類,舉例:int->Integer
3.如果不給泛型類指定具體類型,此時泛型類型可以爲任意類型
public class ExampleUnitTest {
@Test
public void exampleTest() {
TestClass<Integer> object1 = new TestClass<>(1);//泛型是Integer類型,int的包裝類
TestClass<String> object2 = new TestClass<>("2");//泛型是String類型
TestClass object3 = new TestClass();//如果不給泛型類指定具體類型,類中使用泛型的成員可以爲任意類型
object3.setMember(123);
System.out.println(object3.member);
object3.setMember("123");//
System.out.println("exampleTest: " + object1.getMember() + " , " + object2.getMember() + " , " + object3.getMember());
}
public class TestClass<T> {
private T member;
public TestClass() {
}
public TestClass(T member) {
this.member = member;
}
public T getMember() {
return member;
}
public void setMember(T member) {
this.member = member;
}
}
}
輸出
123
exampleTest: 1 , 2 , 123
泛型接口
泛型修飾接口
泛型接口的實現類未傳入具體的泛型實參,此時應該將泛型同樣聲明到類中,否者使用的時候無法確定具體類型
public interface TestInterface1<R> {
void callback(R r);
}
public class ExampleClass4<R> implements TestInterface1<R> {
//泛型接口的實現類未傳入具體的泛型,舉例<T>,此時應該將泛型同樣聲明到類中
@Override
public void callback(R r) {
System.out.println(r);
}
}
public class ExampleUnitTest {
@Test
public void exampleTest2(){
ExampleClass4<String> exampleClass4 = new ExampleClass4<>();
exampleClass4.callback("String類型");
}
}
打印
String類型
泛型接口的實現類傳入具體的泛型實參,舉例 < String>,使用對應泛型的地方都應該爲對應類型
public class ExampleClass5 implements TestInterface1<String> {
@Override
public void callback(String s) {
System.out.println(s);
}
}
public class ExampleUnitTest {
@Test
public void exampleTest2(){
ExampleClass5 exampleClass5 = new ExampleClass5();
exampleClass5.callback("exampleClass5");
}
}
打印
exampleClass5
如果泛型接口有兩個泛型參數的時候,當泛型接口的實現類傳入了具體的泛型實參,情況和一個泛型參數的一樣,使用對應泛型的參數或成員都應該爲對應類型
這裏注意的是當泛型接口的實現類沒有傳入具體的泛型實參的情況
泛型接口的實現類未傳入具體的泛型,舉例<T,R>,此時應該將泛型同樣聲明到類中
alt+enter默認會在ExampleClass2聲明第一個泛型T,如果想要使用泛型R,也需要在ExampleClass2類上聲名,否者會報錯,見以下例子
public interface TestInterface2<T, R> {
void callback(T t);
R onNext();
}
public class ExampleClass2<T> implements TestInterface2<T, R> {
//泛型接口的實現類未傳入具體的泛型,舉例<T,R>,此時應該將泛型同樣聲明到類中
//alt+enter默認會在ExampleClass2聲明第一個泛型T,如果想要使用泛型R,也需要在ExampleClass2類上聲名
private R member;
@Override
public void callback(T t) {
System.out.println(t);
}
@Override
public R onNext() {
return getMember();
}
private R getMember() {
return member;
}
public void setMember(R member) {
this.member = member;
}
}
具體使用的時候會報錯cannot be applied to …
所以如果外部需要使用到第二個泛型參數,同樣也需要聲明到類上
public class ExampleClass3<T,R> implements TestInterface2<T, R> {
//泛型接口的實現類未傳入具體的泛型,舉例<T,R>,此時應該將泛型同樣聲明到類中
private R member;
@Override
public void callback(T t) {
System.out.println(t);
}
@Override
public R onNext() {
return getMember();
}
private R getMember() {
return member;
}
public void setMember(R member) {
this.member = member;
}
}
public class ExampleUnitTest {
@Test
public void exampleTest2(){
ExampleClass3<String, Boolean> exampleClass3 = new ExampleClass3<>();
exampleClass3.callback("傳遞String");
exampleClass3.setMember(true);
Boolean aBoolean = exampleClass3.onNext();
}
}
泛型方法
1.在訪問權限和返回值類型之間聲明方法的泛型,注意,這個和泛型類 TestMethod的T不是同一個,可以是相同類型也可以是不同類型,當然中的T可以換成任意字母
2.聲明瞭泛型的方法纔是泛型方法,僅在方法中,沒有聲明的不是泛型方法,比如當前類的構造函數public TestMethod(T t){},就不是泛型方法
3.泛型方法中聲明的泛型,可以在方法中使用該泛型,反之不能使用該泛型
例子:
public class TestMethod<T> {
private T mT;
public TestMethod(T t) {
this.mT = t;
}
public T getmT() {
return mT;
}
/**
* 泛型方法
* 1.在訪問權限和返回值類型之間聲明方法的泛型<T>,注意,這個<T>和泛型類 TestMethod<T>的T不是同一個,可以是相同類型也可以是不同類型,取決於泛型實參傳的是什麼類型,可以見例子ExampleUnitTest,當然<T>中的T可以換成任意字母
* 2.聲明瞭泛型<T>的方法纔是泛型方法,僅在方法中,沒有聲明的不是泛型方法,比如當前類的構造函數public TestMethod(T t){},就不是泛型方法
* 3.泛型方法中聲明的泛型<T>,可以在方法中使用該泛型,反之不能使用該泛型
* @param t
* @param <T>
*/
public <T> void myMethod1(TestMethod<T> t){
T t1 = t.getmT();
System.out.println(t1+","+t1.getClass().toString());
}
/**
* 不是泛型方法,只是在參數中使用了類中聲明的泛型T,此處如果調用該方法,只能傳和類一致的泛型
* @param t
*/
public void myMethod2(TestMethod<T> t){
T t1 = t.getmT();
System.out.println(t1+","+t1.getClass().toString());
}
}
public class ExampleUnitTest {
@Test
public void test() {
TestMethod<String> testMethod1 = new TestMethod<>("泛型方法");
TestMethod<Integer> testMethod2 = new TestMethod<>(123);
/**
* 下面方法調用正常
* 因爲在方法中聲明瞭新泛型,可以在方法中使用
*/
testMethod1.myMethod1(testMethod1);
/**
* 下面報錯:TestMethod<String> in TestMethod cannot be applied to TestMethod<Integer>
* 因爲myMethod2使用的類中聲明的泛型,當前是String類型的,而傳入Integer,所以報錯
*/
testMethod1.myMethod2(testMethod2);
}
}
註釋掉testMethod1.myMethod2(testMethod2);,運行
泛型方法,class java.lang.String
注意:如果在靜態方法中使用泛型,這個靜態方法必須設置成靜態方法
泛型通配符
<?>可以傳入任意類型,例如在上一節泛型方法中舉的例子,在myMethod2中使用的是類中的泛型,對象testMethod1對應類中的泛型是String,而想傳入泛型是Integer的對象,在不是泛型方法的情況下是行不通的,這裏如果我們在方法參數上使用泛型通配符,就可以解決這個問題
例子:
/**
* 泛型通配符,可以傳入任意類型
* @param t
*/
public void myMethod3(TestMethod<?> t){
Object o = t.getmT();
System.out.println(o+" , "+o.getClass().toString());
}
@Test
public void test() {
TestMethod<String> testMethod1 = new TestMethod<>("泛型方法");
TestMethod<Integer> testMethod2 = new TestMethod<>(123);
/**
* 下面方法調用正常
* 因爲在方法中聲明瞭新泛型,可以在方法中使用
*/
// testMethod1.myMethod1(testMethod1);
/**
* 下面報錯:TestMethod<String> in TestMethod cannot be applied to TestMethod<Integer>
* 因爲myMethod2使用的類中聲明的泛型,當前是String類型的,而傳入Integer,所以報錯
*/
// testMethod1.myMethod2(testMethod2);
testMethod1.myMethod3(testMethod2);
}
輸出
123 , class java.lang.Integer
泛型上下邊界
在使用泛型的時候,可以限制泛型實參的上下邊界,也就是:
指定泛型的父類是某種類型就是限制泛型的上邊界,只能使用上邊界類型和上邊界內的類型,例如< ? extends TestClass1>,上邊界是TestClass1,可以使用泛型TestClass1和TestClass1的子類泛型;
指定泛型的子類是某種類型就是限制泛型的下邊界,只能使用下邊界類型的泛型和下邊界以內的泛型,例如< ? super TestClass2 > 下邊界是TestClass2,只能使用泛型爲TestClass2和TestClass2的父類泛型
舉例
水果、蘋果、綠蘋果的例子,水果是超類,蘋果繼承水果,綠蘋果繼承蘋果;
定義一個泛型容器Generic,
eat2(Generic<? extends Apple> t) 方法是確定了上邊界爲Apple,所以只能傳的泛型爲Apple或者GreenApple
eat3(Generic<? super Apple> t) 方法是確定了下邊界爲Apple,所以只能傳的泛型爲Apple或者Friut
public class Fruit {
private String name;
public Fruit() {
}
public Fruit(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class Apple extends Fruit {
public Apple() {
}
public Apple(String name) {
super(name);
}
@Override
public String toString() {
return "Apple{}";
}
}
public class GreenApple extends Apple {
public GreenApple() {
}
public GreenApple(String name) {
super(name);
}
@Override
public String toString() {
return "GreenApple{}";
}
}
public class Generic<T> {
private T t;
public Generic() {
}
public Generic(T t) {
this.t = t;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
public String eat2(Generic<? extends Apple> t){
return t.getT().toString();
}
public String eat3(Generic<? super Apple> t){
return t.getT().toString();
}
}
@Test
public void exampleTest3() {
Generic<Fruit> fruitGeneric = new Generic<>();
Generic<Apple> appleGeneric = new Generic<>();
Generic<GreenApple> greenAppleGeneric = new Generic<>();
Fruit fruit = new Fruit("水果");
Apple apple = new Apple("蘋果");
GreenApple greenApple = new GreenApple("綠蘋果");
fruitGeneric.setT(fruit);
appleGeneric.setT(apple);
greenAppleGeneric.setT(greenApple);
//Apple是上邊界 可以傳Generic中泛型繼承於Apple的子類以及Apple
System.out.println(appleGeneric.eat2(greenAppleGeneric));
System.out.println(appleGeneric.eat2(appleGeneric));
//Apple是下邊界 可以傳Generic中Apple泛型的父類以及Apple
System.out.println(appleGeneric.eat3(fruitGeneric));
System.out.println(appleGeneric.eat3(appleGeneric));
}
打印
GreenApple{}
Apple{}
com.himi.test.Fruit@3b95a09c
Apple{}
結束語
先寫一下泛型基本使用,過些日子寫一下其它用法和注意事項