Java基礎教程——泛型


泛型

Generics:泛型,願意指“無商標的”。

泛型,可以理解為“寬泛的數據類型”,就是將類型由原來的具體的類型泛化。

泛型在建立對象時不指定類中屬性的具體類型,而是在聲明及實例化對象時由外部指定。泛型可以提高數據安全性。

List中應用了泛型,在編譯期對數據類型進行嚴格 檢查,如果類型不匹配,編譯無法通過。
示例 :

public interface List<E> extends Collection<E>

E:Element

T:Type


泛型的本質是為了參數化類型,即在不創建新類型的情況下,通過泛型指定的不同類型(類型形參),調用時傳入具體的類型(類型實參)。

在使用泛型過程中,操作的數據類型被指定為一個參數,這種參數類型可以用在類、接口和方法中,分別被稱為泛型類、泛型接口、泛型方法。

泛型類

public class 泛型_類 {
	public static void main(String[] args) {
		車<Girl> v = new 車<Girl>();
		v.add(new Girl());
		// 不讓上車
		// v.add(new Boy());
	}
}
abstract class Person {
}
class Boy extends Person {
}
class Girl extends Person {
}
class 車<T> {
	public T add(T arg) {
		System.out.println(arg.getClass());
		return arg;
	}
}

泛型接口

泛型接口與泛型類的定義及使用基本相同。

public interface Generator<T> {
    public T getS();
}

泛型接口的兩種使用方法(以ArrayList和Scanner為例):

import java.util.*;
interface IGenerics<E> {
	void m(E e);
}
// 仿照ArrayList<E>,implements List<E>
// 實現類還是泛型
class MyList<E> implements IGenerics<E> {
	@Override
	public void m(E e) {
		List<E> lst = new ArrayList<>();
	}
}
// 仿Scanner類, implements Iterator<String>
// 直接指定泛型為String
class MyScanner implements IGenerics<String> {
	@Override
	public void m(String e) {
		Scanner sc = new Scanner(System.in);
	}
}
public class 泛型接口 {
	public static void main(String[] args) {
		// 定義時未指定類型,實例化需要指定類型
		MyList<Integer> my1 = new MyList<>();
		my1.m(1);
		// 定義時已經指定類型,實例化不用指定
		MyScanner my2 = new MyScanner();
		my2.m("A");
	}
}

泛型方法

泛型定義在修飾符和返回類型之間,在參數列表中使用泛型。
泛型方法主要針對參數有泛型(如集合)的場景,如果把下例的參數List改為數組則和Object沒有太大區別

import java.util.*;
public class 泛型方法 {
	public static <T> int find(List<T> lst, T a) {
		int indexOf = -1;
		if (lst != null) {
			indexOf = lst.indexOf(a);
		}
		return indexOf;
	}
	public static void main(String[] args) {
		int find = find(new ArrayList<String>(), "A");
		System.out.println(find);
		// 編譯不通過:find(new ArrayList<Integer>(), "A");
	}
}

說明:

泛型的參數類型只能是類類型,不能是簡單類型。比如,<int>是不可使用的。
可以聲明多個泛型參數類型,比如<T,P,Q…>,同時還可以嵌套泛型,例如:<List<String>>。
泛型的參數類型可以使用extends語句,例如<T extends superclass>。
使用extends語句將限制泛型參數的適用范圍,只能是指定類型的子類。
|-例如:<T extends Collection> ,則表示該泛型參數的使用范圍是所有實現了Collection接口的calss。如果傳入<String>則程序編譯出錯。

示例【T extends Collection】:

import java.util.*;
public class 泛型extends {
	public static <A extends Collection> void find(A arg1, int arg2) {
	}
	public static void main(String[] args) {
		find(new ArrayList(), 5);
		// 不是Collection的子類,編譯不通過:
		// find(new String(), 5);
	}
}

<?>通配符

用於承接不同類型的泛型對象。

不知道使用什么類型接收數據時,使用?通配符。

此時只能接收數據,不能往該集合中存儲數據。

import java.util.*;
public class 泛型_通配符 {
	public static void main(String[] args) {
		List<String> lst1 = new ArrayList<String>();
		lst1.add("A");
		List<Integer> lst2 = new ArrayList<Integer>();
		lst2.add(1);
		printList_T(lst1);
		printList_T(lst2);
		System.out.println("----------");
		printList(lst1);
		printList(lst2);
	}
	// 用常規泛型寫,比較麻煩
	private static <T> void printList_T(List<T> list) {
		list.add(list.get(0));
		for (int i = 0; i < list.size(); i++) {
			Object o = list.get(i);
			System.out.println(o);
		}
	}
	// 用泛型通配符寫簡單
	private static void printList(List<?> list) {
		// ?不能寫入,此處編譯錯誤:list.add(list.get(0));
		for (int i = 0; i < list.size(); i++) {
			Object o = list.get(i);
			System.out.println(o);
		}
	}
}

通配符界限

<? extends T>:是 “上界通配符(Upper Bounds Wildcards)”,泛型只能是T的子類/本身
<? super T>:是 “下界通配符(Lower Bounds Wildcards)”,泛型只能是T的父類/本身
參見:泛型通配符界限問題: https://www.cnblogs.com/tigerlion/p/10659515.html

定義三個類,Fruit->Apple->RedApple,聲明ArrayList,有如下現象:

import java.util.*;
public class 泛型_通配符邊界2 {
	public static void main(String[] args) {
		ArrayList<Apple> p1 = new ArrayList<Apple>();
		// 【震驚!裝得了蘋果,裝不了紅蘋果】
		// ↓Type mismatch:
		// cannot convert from ArrayList<RedApple> to ArrayList<Apple>
		// ArrayList<Apple> p2 = new ArrayList<RedApple>();
	}
}
class Fruit {
}
class Apple extends Fruit {
}
class RedApple extends Apple {
}

解決方案就是

【1.<? extends Fruit>,定類型繼承關系的上界】
【2.<? super Apple>,確定類型繼承關系的下界】

解決方案如下,照着注釋看便是

package ah;
import java.util.*;
// 先定義三個類:水果、蘋果、紅蘋果
class Fruit {
}
class Apple extends Fruit {
}
class RedApple extends Apple {
}
public class 泛型_通配符邊界 {
	public static void main(String[] args) {
		ArrayList<Apple> p1 = new ArrayList<Apple>();
		// 【震驚!裝得了蘋果,裝不了紅蘋果】
		// ↓Type mismatch:
		// cannot convert from ArrayList<RedApple> to ArrayList<Apple>
		// ArrayList<Apple> p2 = new ArrayList<RedApple>();
		// 【1.<? extends Fruit>,定類型繼承關系的上界:】
		// 能裝Apple以及一切Apple的派生類
		ArrayList<? extends Apple> p3 = new ArrayList<Apple>();
		p3 = new ArrayList<RedApple>();
		// ↓上層的類不接受
		// Type mismatch:
		// cannot convert from ArrayList<Fruit> to ArrayList<? extends Apple>
		// p3 = new ArrayList<Fruit>();
		// 【然而,extends是只讀的,不能寫入】
		// p3.add(new Apple());
		if (p3.size() != 0) {
			Apple apple = p3.get(0);
		}
		// 【2.<? super Apple>,確定類型繼承關系的下界】
		// 能裝蘋果以及一切蘋果的基類
		ArrayList<? super Apple> p4 = new ArrayList<Apple>();
		p4 = new ArrayList<Fruit>();
		p4 = new ArrayList<Apple>();
		// Type mismatch:
		// cannot convert from ArrayList<RedApple> to ArrayList<? super Apple>
		// p4 = new ArrayList<RedApple>();
		// 【可讀可寫,讀出來的是Object類型】
		p4.add(new RedApple());// 子類對象但是可以寫進入,因為默認向上轉型
		Object object = p4.get(0);
		System.out.println(object);
		// 最后,其實JDK 7之后,后面的<>里什么都不用寫
		List<Apple> p2 = new ArrayList<>();
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM