泛型
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<>();
}
}