泛型
15.2 簡單泛型##
泛型的主要目的之一就是用來指定容器要持有什麼類型的對象,而且由編譯器來來保證正確性。
// 類型參數T
public class Holder3<T> {
private T t;
public Holder3(T a) {
this.t = a;
}
public void set(T a){
this.t = a;
}
public T get() {
return t;
}
public static void main(String[] args) {
Holder3<Automobile> h2 = new Holder3<Automobile>(new Automobile());
Automobile a = (Automobile) h2.get();
}
}
使用類型參數T可以暫時不指定類型,在創建對象時必須指明類型。
- 一個元組類庫
將一組對象打包存儲咋一個對象中稱爲元組。
/**
* 元組持有數據
* @author Administrator
*
*/
public class TwoTuple<A,B> {
public final A first;
public final B second;
public TwoTuple(A a,B b){
first = a;
second = b;
}
@Override
public String toString() {
return "("+first+","+second+")";
}
}
可以通過元組,返回一組對象。
2. 一個堆棧類
// 自定義堆棧
public class LinkedStack<T> {
private static class Node<U> {
U item;
Node<U> next;
Node() {
item = null;
next = null;
}
Node(U item,Node<U> next){
this.item = item;
this.next = next;
}
boolean end(){
return item == null && next == null;
}
}
private Node<T> top = new Node<T>();
public void push(T item){
top = new Node<T>(item, top);
}
public T pop(){
T result = top.item;
if(!top.end()){
top = top.next;
}
return result;
}
public static void main(String[] args) {
LinkedStack<String> s = new LinkedStack<String>();
for (String string : "Phasers on stun!".split("")) {
s.push(string);
}
Print.print(s.pop());
}
}
使用了末端哨兵來判斷堆棧是否爲空。
3. RandomList
15.3 泛型接口##
泛型也可用於接口。
public interface Generator<T> {
T next();
}
泛型接口應用:
生成器利用工程模式生成對象。
利用適配器模式可以實現迭代功能。
15.4 泛型方法##
使用泛型了時,在創建對象時必須指明類型參數;使用泛型方法時,不必指明參數類型。編譯器會找出具體的類型,被稱爲參數推斷。
定義泛型方法,只需將泛型參數列表置於返回值之前。
//泛型方法
public <T> void f(T t) {
System.out.println(t.getClass().getName());
}
- 槓桿利用參數推斷
類型推斷只對賦值有效,作爲參數傳遞無效。 - 可變參數與泛型方法
//可變參數與泛型方法可以很好的共存
public class GenericVarargs {
public static <T> List<T> makeList(T... args) {
List<T> result = new ArrayList<T>();
for (T t : args) {
result.add(t);
}
return result;
}
public static void main(String[] args) {
List<String> list = makeList("A");
System.out.println(list);
list = makeList("A","B","C");
System.out.println(list);
}
}
15.5 擦除的神祕之處##
class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Practice<POSITION,MOMENTUM>{}
public class LostInformation {
public static void main(String[] args) {
List<Frob> list = new ArrayList<Frob>();
Map<Frob,Fnorkle> map = new HashMap<Frob,Fnorkle>();
Quark<Fnorkle> quark = new Quark<Fnorkle>();
Practice<Long,Double> practice = new Practice<Long,Double>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));
System.out.println(Arrays.toString(practice.getClass().getTypeParameters()));
}
}
輸出:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
在泛型代碼內部,無法獲得任何有關泛型參類型的信息。
擦除的代價是:泛型不能顯式地應用運行時類型操作。因爲所有參數 類型信息都丟失了。
15.5 擦除的補償##
泛型擦除無法在運行時獲取類型信息,可以通過傳入類型解決。
/**
* 通過傳入Class獲取類型
* @author Administrator
*/
class Building{}
class House extends Building{}
public class ClassTypeCapture<T> {
Class<T> kind;
public ClassTypeCapture(Class<T> kind) {
this.kind = kind;
}
public boolean f(Object arg) {
return kind.isInstance(arg);
}
public static void main(String[] args) {
ClassTypeCapture<House> ct2 =
new ClassTypeCapture<House>(House.class);
System.out.println(ct2.f(new Building()));
System.out.println(ct2.f(new House()));
}
}
泛型擦除創建對象方式:工廠方式和模板方式。
不能創建泛型數組解決方式:使用ArrayList。
15.6 邊界##
邊界使得你可以在用於泛型的參數類型上設置條件。
<T extends 接口名或類名>
15.7 通配符##
< ? extends 接口名或類名>
任何從某個類繼承的類型都適用。
< ? super 接口名或類名> 超類型通配符
無界通配符:<?>
15.8 問題##
- 任何基本數據類型都不能作爲類型參數
- 實現參數化接口
一個類不能實現同一個泛型接口的兩種變體。 - 轉型和警告
- 重載
由於泛型擦除的原因,重載方法將產生相同簽名。 - 基類劫持了接口
15.9 自限定類型##
ClassA extens ClassB<A>
自限定會強制要求將正在定義的類當做參數傳遞給基類。
15.10 動態類型安全##
1.5以後可以使用Collections工具類中的checkedList(list, type),checkedMap()等方法檢查傳入容器的類型。
15.11 異常##
類型參數可應用於一個方法的throws子句中。
15.12 混型##
混型:混個多個類的能力,產生一個可以表示混型中所有類型的類。
- 與接口混合
使用代理方式,每個混入類型中都有相應的域,使用時代理調用方法。 - 裝飾器模式
- 與動態代理結合
15.13 潛在類型機制##
class Dog:
def speak(self):
print "atf:
def sit(self):
print "Sitting"
def reproduce(self):
pass
class Robot:
def speak(self):
print "atf:
def sit(self):
print "Sitting"
def reproduce(self):
pass
a = Dog()
b = Robot()
perform(a)
perform(b)
perform(anything) 沒有針對任何anything類型,anything包含的接口是潛在的。
Java不支持潛在類型機制。
15.17 對缺乏潛在類型機制的補償##
- 反射
利用反射可以動態地確定所需要的方法並調用它。 - 將方法應用於序列
反射提供了一些有趣的可能,但它將所有的類型檢查都轉移到了運行時。
嘗試一些實現編譯時期檢查。 - 當你並未擁有正確接口時
編譯時期可以檢查類型,但是類型被限制在繼承層次之內。 - 用適配器仿真潛在類型
適配器模式模仿潛在類型機制,並在編譯時期檢查類型。
/**
* 適配器模式實現潛在類型機制
* @author Administrator
*
* @param <T>
*/
interface Addable<T> { void add(T t);}
class AddableCollectionAdapter<T> implements Addable<T>{
private Collection<T> c;
public AddableCollectionAdapter(Collection<T> c) {
this.c = c;
}
@Override
public void add(T t) {
c.add(t);
}
}
class Adapter{
public static<T> Addable<T> collectionAdapter(Collection<T> c) {
return new AddableCollectionAdapter(c);
}
}
class AddableSimpleQueue<T> extends SimpleQueue<T> implements Addable<T>{
public void add(T t) {
super.add(t);
}
}
public class Fill2 {
public static<T> void fill(Addable<T> addable,
Class<? extends T> classToken,int size) {
for (int i = 0; i < size; i++) {
try {
addable.add(classToken.newInstance());
} catch (Exception e) {
throw new RuntimeException();
}
}
}
public static<T> void fill(Addable<T> addable,
Generator< T> generator,int size) {
for (int i = 0; i < size; i++) {
try {
addable.add(generator.next());
} catch (Exception e) {
throw new RuntimeException();
}
}
}
public static void main(String[] args) {
List<Coffee> coffees = new ArrayList<>();
Fill2.fill(new AddableCollectionAdapter<Coffee>(coffees), Coffee.class, 3);
Fill2.fill(Adapter.collectionAdapter(coffees), Coffee.class, 3);
for (Coffee coffee : coffees) {
System.out.println(coffee);
}
System.out.println("--------------------");
AddableSimpleQueue<Coffee> coffeequeue = new AddableSimpleQueue();
Fill2.fill(coffeequeue, Mocha.class, 5);
Fill2.fill(coffeequeue, Latte.class, 6);
for (Coffee coffee : coffeequeue) {
System.out.println(coffee);
}
}
}
15.18 將函數對象用作策略##
策略設計模式將變化的事物隔離到函數對象中。
15.19 總結##
泛型的作用除了提供類型檢查,還可以編寫更"泛化"的代碼。