泛型
對象和實(shí)例是一個(gè)意思陵究,類與對象的關(guān)系就像數(shù)據(jù)類型和變量一樣琴庵。
-
泛型的主要目的之一就是用來指定類(如:容器)要持有什么類型的對象衰抑,代碼解釋如下:
public class Holder3<T> { private T a;//持有了T的對象,此處可以持有任何對象秋泄,可以用Object代替但是要向下轉(zhuǎn)型 public Holder3(T a) { this.a = a; } public void set(T a) { this.a = a; } public T get() { return a; } public static void main(String[] args) { Holder3<Automobile> h3 = new Holder3<Automobile>(new Automobile()); Automobile a = h3.get(); // No cast needed // h3.set("Not an Automobile"); // Error // h3.set(1); // Error } } ///:~
-
在有些場景中會有一個(gè)方法返回多個(gè)對象悼瘾,你可以使用創(chuàng)建類用它來持有返回的多個(gè)對象,如果再 加上泛型技術(shù)就會在編譯期確保類型安全书闸。代碼解釋如下:
//: net/mindview/util/TwoTuple.java package net.mindview.util; public class TwoTuple<A,B> { public final A first; public final B second; public TwoTuple(A a, B b) { first = a; second = b; } public String toString() { return "(" + first + ", " + second + ")"; } } ///:~
如果泛型用得好尼变,基本上不用強(qiáng)制性轉(zhuǎn)換
-
泛型也可以應(yīng)用于接口,比如
public interface Generator<T>
,在寫繼承的時(shí)候T
可以寫成任意類型,比如構(gòu)造一個(gè)咖啡工廠public class CoffeeGenerator implements Generator<Coffee>
,構(gòu)造一個(gè)生成Fibonacci數(shù)列的類public class Fibonacci implements Generator<Integer>
嫌术,咖啡代碼如下://... 省略處為一些簡單類哀澈,如Latte,Mocha,Cappuccino,Americano,Breve這些類都繼承于coffee,coffee.java如下 package com.generics.coffee; public class Coffee { private static long counter = 0; private final long id = counter++; public String toString() { return getClass().getSimpleName() + " " + id; } } ///:~ //: generics/coffee/CoffeeGenerator.java // Generate different types of Coffee: package com.generics.coffee; import java.util.*; import net.mindview.util.*; public class CoffeeGenerator implements Generator<Coffee> ,Iterable<Coffee> { @SuppressWarnings("rawtypes") private Class[] types = { Latte.class, Mocha.class, Cappuccino.class, Americano.class, Breve.class, }; private static Random rand = new Random(47); public CoffeeGenerator() {} // For iteration: private int size = 0; public CoffeeGenerator(int sz) { size = sz; } public Coffee next() { try { return (Coffee) types[rand.nextInt(types.length)].newInstance(); // Report programmer errors at run time: } catch(Exception e) { throw new RuntimeException(e); } } //解釋:內(nèi)部類實(shí)現(xiàn)迭代器,該實(shí)現(xiàn)了Iterator而CoffeeGenerator實(shí)現(xiàn)的是Iterable,要實(shí)現(xiàn)foreach循環(huán)必須實(shí)現(xiàn)這兩個(gè)接口, //從代碼看起來foreach循環(huán)是看出來了度气,要理解其本質(zhì)的原理需要看jvm里面的字節(jié)碼割按,new CoffeeGenerator(5)調(diào)用后,首先產(chǎn)生 //CoffeeIterator的實(shí)例磷籍,執(zhí)行hasNext()->next() //此處可以也可以用匿名內(nèi)部類 class CoffeeIterator implements Iterator<Coffee> { int count = size; public boolean hasNext() { return count > 0; } public Coffee next() { count--; return CoffeeGenerator.this.next(); } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; public Iterator<Coffee> iterator() { return new CoffeeIterator(); } public static void main(String[] args) { CoffeeGenerator gen = new CoffeeGenerator(); for(int i = 0; i < 5; i++) System.out.println(gen.next()); for(Coffee c : new CoffeeGenerator(5)) System.out.println(c); } } /* Output: Americano 0 Latte 1 Americano 2 Mocha 3 Mocha 4 Breve 5 Americano 6 Latte 7 Cappuccino 8 Cappuccino 9 *///:~
Fibonacci數(shù)列的代碼如下:
package com.generics; import net.mindview.util.*; public class Fibonacci implements Generator<Integer> { private int count = 0; public Integer next() { return fib(count++); } private int fib(int n) { if(n < 2) return 1; return fib(n-2) + fib(n-1); } public static void main(String[] args) { Fibonacci gen = new Fibonacci(); for(int i = 0; i < 18; i++) System.out.print(gen.next() + " "); } } /* Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 *///:~
如果想要實(shí)現(xiàn)迭代适荣,而且要不用內(nèi)部類的方式(
CoffeeGenerator.java
使用的是內(nèi)部類實(shí)現(xiàn)的迭代器模式),用適配器模式實(shí)現(xiàn)院领,適配器模式即把兩個(gè)互不相關(guān)的接口或者類相連接弛矛,所以可以使用繼承或者組合,UML如下:![](https://www.processon.com/chart_image/thumb/58aaf16ee4b071585833fd96.png) 迭代如下:
package com.generics; // Adapt the Fibonacci class to make it Iterable. import java.util.*; //組合來創(chuàng)建適配器 public class IterableFibonacci implements Iterable<Integer> { private Fibonacci fibonacci = new Fibonacci(); private int n; public IterableFibonacci(int count) { n = count; } public Iterator<Integer> iterator() { //匿名內(nèi)部類的形式 return new Iterator<Integer>() { @Override public Integer next() { // TODO Auto-generated method stub n--; return fibonacci.next();//invoke next() in Fibonacci,for this extends Fibonacci } @Override public boolean hasNext() { // TODO Auto-generated method stub return n > 0; } public void remove() { // Not implemented throw new UnsupportedOperationException(); } }; } public static void main(String[] args) { for(int i : new IterableFibonacci(18)) System.out.print(i + " "); } } /* Output: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 *///:~
-
泛型應(yīng)用于方法
應(yīng)用于方法
public <T> void f(T x){}
比然,其中<T>一定要寫丈氓,不然編譯器是無法識別出參數(shù)的T
-
當(dāng)可變參數(shù)與方法結(jié)合:
package com.generics; //: generics/GenericVarargs.java import java.util.*; public class GenericVarargs { //此處的makeList就像是java.util.Arrays里面的asList(T... args); public static <T> List<T> makeList(T... args) { List<T> result = new ArrayList<T>(); for(T item : args) result.add(item); return result; } public static void main(String[] args) { List<String> ls = makeList("A"); System.out.println(ls); ls = makeList("A", "B", "C"); System.out.println(ls); ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split("")); System.out.println(ls); } } /* Output: [A] [A, B, C] [, A, B, C, D, E, F, F, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z] *///:~
從上面代碼注釋可以看到makeList和asList方法很像,下面來看看asList的源碼分析
public static <T> List<T> asList(T... a) { return new ArrayList<>(a);//此處的ArrayList不是你想的java.util.ArrayList,他是Arrays里面的一個(gè)靜態(tài)內(nèi)部類 } //此處是靜態(tài)內(nèi)部類的構(gòu)造器强法,返回一個(gè)數(shù)組万俗,需要說明的是該內(nèi)部類并沒有實(shí)現(xiàn)add,remove等方法,因?yàn)閍sList()方法在大多數(shù)使用場景中是不用改變的饮怯,所以要構(gòu)造一個(gè)可編輯的ArrayList()用類似下面的代碼即可List<WaiterLevel> levelList = new ArrayList<WaiterLevel>(Arrays.asList("a", "b", "c")); ArrayList(E[] array) { a = Objects.requireNonNull(array);//判斷array是否為空 }
-
利用泛型方法對前一章的生成器進(jìn)行更一步的抽象闰歪,代碼如下:
//: net/mindview/util/BasicGenerator.java // Automatically create a Generator, given a class // with a default (no-arg) constructor. package net.mindview.util; //this class can generate any Class which have default constructor by create() function,but there is a limit which is that constructor cannot pass argument(傳參) public class BasicGenerator<T> implements Generator<T> { private Class<T> type; public BasicGenerator(Class<T> type){ this.type = type; } public T next() { try { // Assumes type is a public class: return type.newInstance(); } catch(Exception e) { throw new RuntimeException(e); } } // Produce a Default generator given a type token: public static <T> Generator<T> create(Class<T> type) { return new BasicGenerator<T>(type); } } ///:~
更多的,我們可以對前面提到的元組進(jìn)行進(jìn)一步的抽象
//: net/mindview/util/Tuple.java // Tuple library using type argument inference. package net.mindview.util; public class Tuple { public static <A,B> TwoTuple<A,B> tuple(A a, B b) { return new TwoTuple<A,B>(a, b); } public static <A,B,C> ThreeTuple<A,B,C> tuple(A a, B b, C c) { return new ThreeTuple<A,B,C>(a, b, c); } public static <A,B,C,D> FourTuple<A,B,C,D> tuple(A a, B b, C c, D d) { return new FourTuple<A,B,C,D>(a, b, c, d); } public static <A,B,C,D,E> FiveTuple<A,B,C,D,E> tuple(A a, B b, C c, D d, E e) { return new FiveTuple<A,B,C,D,E>(a, b, c, d, e); } } ///:~
-
java對泛型的擦除有四句話
- 泛型類型在運(yùn)行時(shí)都是Object類型
- 模板只在編譯階段有效是為了提供編譯期的類型安全蓖墅,通過反射操作可以繞過編譯階段
- 在編譯期就可以知道的類型信息是可以操作的
- 所有在運(yùn)行時(shí)才能知道類型信息的操作都將無法工作
package com.generics; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; class Manipulator<T> { public T obj; public Manipulator(T x) { obj = x; } // Error: cannot find symbol: method f(): public void manipulate() { // (obj).f(); } public void erase2(){ //T[] t = new T[4]; //a instanceof T; } } public class Manipulation { public static void main(String[] args) { //1.模板只在編譯階段有效是為了提供編譯期的類型安全库倘,通過反射操作可以繞過編譯階段 List<String> list1 = new ArrayList<>(); List list = new ArrayList<>(); list1.add("s"); //list1.add(2); try { Method m = ArrayList.class.getMethod("add",Object.class); m.invoke(list1, 2); System.out.println(list1); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } //2.在編譯期就可以知道的類型信息是可以操作的 HasF hf = new HasF(); Manipulator<HasF> manipulator = new Manipulator<HasF>(hf); manipulator.obj.f(); //在這個(gè)函數(shù)里面調(diào)用的obj.f(),是不可能考慮客戶端的類型,即是單獨(dú)去編譯的置媳,在客戶端沒有調(diào)用時(shí)于樟,他并不知道T是什么類型,所以有錯(cuò) manipulator.manipulate(); //3.所有在運(yùn)行時(shí)才能知道類型信息的操作都將無法工作 manipulator.erase2(); } } ///:~
不能創(chuàng)建泛型類型數(shù)組的,一般的解決方案是在任何想要?jiǎng)?chuàng)建泛型數(shù)組的地方都是用ArrayList去創(chuàng)建拇囊。
泛型的主要目標(biāo)之一是將錯(cuò)誤檢測移入到編譯期
-
編譯器直接拒絕對參數(shù)列表中涉及到的通配符的方法迂曲,即
?add(? extends fruit)
如果變成了這樣結(jié)果如下圖 -
下面代碼能夠?qū)崿F(xiàn)只能存水果的集合,且在編譯期內(nèi)就能檢查類型信息,其中第二種方式稱之為逆變寥袭。為什么逆變的方式可以實(shí)現(xiàn)路捧?答:super關(guān)鍵字表示下界,
List<? super Apple> fruit = new ArrayList<>();
传黄,而?必須要表示一個(gè)確切的類型杰扫,準(zhǔn)確來講應(yīng)該是這樣聲明一個(gè)實(shí)例即:List<? super Apple> fruit = new ArrayList<在這個(gè)括號內(nèi)部必須是Apple的父類>();
即在比如List<? super Apple> fruit = new ArrayList<Fruit>()
,所以當(dāng)add()
的時(shí)候膘掰,可以插入Apple的子類章姓,同樣的道理分析List<? extends Apple> flist2 = new ArrayList
<這里面要插入的是Apple的子類>();所以當(dāng)add(new Apple())
時(shí)候佳遣,會失敗,比如List<? extends Apple> flist2 = new ArrayList<Jonathan>();Jonathan = new Apple()//error;
//1.想要實(shí)現(xiàn)一個(gè)集合里面能裝所有類型的水果凡伊,但是在編譯期不允許裝除了水果以外的其他對象 List<Fruit> flist3 = new ArrayList<>(); flist3.addAll(Arrays.asList(new Apple())); flist3.addAll(Arrays.asList(new Orange())); System.out.println(flist3.get(1)); //2.第一種方式太復(fù)雜,下面用逆變的方式實(shí)現(xiàn) List<? super Fruit> fruit = new ArrayList<>(); fruit.add(new Apple()); fruit.add(new Orange());
-
混型即
Timestamped<Serialnumbered<Basic>> mixin
其中mixin
能夠調(diào)用基類的所有函數(shù)零渐,在C++中,這是顯然的系忙,但是在java中可以這樣聲明诵盼,但不能調(diào)用基類的任何函數(shù)只能調(diào)用Timestamped類中的函數(shù),所以必須使用有些設(shè)計(jì)模式來代替,其中涉及到裝飾器模式银还,和用動(dòng)態(tài)代理(即我們可以動(dòng)態(tài)注入類方法)來實(shí)現(xiàn)混合风宁,但是結(jié)果都沒有C++中方便直接。implements
和extends
關(guān)鍵字實(shí)現(xiàn):package com.generics; //: generics/Mixins.java import java.util.*; interface TimeStamped { long getStamp(); } class TimeStampedImp implements TimeStamped { private final long timeStamp; public TimeStampedImp() { timeStamp = new Date().getTime(); } public long getStamp() { return timeStamp; } } interface SerialNumbered { long getSerialNumber(); } class SerialNumberedImp implements SerialNumbered { private static long counter = 1; private final long serialNumber = counter++; public long getSerialNumber() { return serialNumber; } } interface Basic { public void set(String val); public String get(); } class BasicImp implements Basic { private String value; public void set(String val) { value = val; } public String get() { return value; } } // for Mixin2.java,Timestamped<Serialnumbered<Basic>> mixin = new Timestamped();,mixin can not invoke set() function of Basic,but c++ is capable to do it. //so in java, use implements and extends keywords to realize it. class Mixin extends BasicImp implements TimeStamped, SerialNumbered { //if use this,you must have a instance of response to interface private TimeStamped timeStamp = new TimeStampedImp(); private SerialNumbered serialNumber = new SerialNumberedImp(); public long getStamp() { return timeStamp.getStamp(); } public long getSerialNumber() { return serialNumber.getSerialNumber(); } } public class Mixins { public static void main(String[] args) { Mixin mixin1 = new Mixin(), mixin2 = new Mixin(); mixin1.set("test string 1"); mixin2.set("test string 2"); System.out.println(mixin1.get() + " " + mixin1.getStamp() + " " + mixin1.getSerialNumber()); System.out.println(mixin2.get() + " " + mixin2.getStamp() + " " + mixin2.getSerialNumber()); } } /* Output: (Sample) test string 1 1132437151359 1 test string 2 1132437151359 2 *///:~
裝飾器模式實(shí)現(xiàn)(并沒有完全實(shí)現(xiàn)):
package com.generics.decorator; //: generics/decorator/Decoration.java import java.util.*; class Basic { private String value; public void set(String val) { value = val; } public String get() { return value; } } class Decorator extends Basic { protected Basic basic; public Decorator(Basic basic) { this.basic = basic; } public void set(String val) { basic.set(val); } public String get() { return basic.get(); } } class TimeStamped extends Decorator { private final long timeStamp; public TimeStamped(Basic basic) { super(basic); timeStamp = new Date().getTime(); } public long getStamp() { return timeStamp; } } class SerialNumbered extends Decorator { private static long counter = 1; private final long serialNumber = counter++; public SerialNumbered(Basic basic) { super(basic); } public long getSerialNumber() { return serialNumber; } } //this is decoration design patterns public class Decoration { public static void main(String[] args) { TimeStamped t = new TimeStamped(new Basic()); // because timestamped extends Basic t.set("fasdfa"); //realize such as TimeStamped<SerialNumbered<Basic>> mixin1, mixin2 TimeStamped t2 = new TimeStamped( new SerialNumbered(new Basic())); //! t2.getSerialNumber(); // Not available, obviously SerialNumbered s = new SerialNumbered(new Basic()); SerialNumbered s2 = new SerialNumbered( new TimeStamped(new Basic())); //! s2.getStamp(); // Not available } } ///:~
動(dòng)態(tài)代理模式實(shí)現(xiàn):
package com.generics; //: generics/DynamicProxyMixin.java import java.lang.reflect.*; import java.util.*; import net.mindview.util.*; import static net.mindview.util.Tuple.*; class MixinProxy implements InvocationHandler { Map<String,Object> delegatesByMethod; public MixinProxy(TwoTuple<Object,Class<?>>... pairs) { delegatesByMethod = new HashMap<String,Object>(); for(TwoTuple<Object,Class<?>> pair : pairs) { for(Method method : pair.second.getMethods()) { String methodName = method.getName(); System.out.println(methodName + "()"); // The first interface in the map // implements the method. if (!delegatesByMethod.containsKey(methodName)) delegatesByMethod.put(methodName, pair.first);// this is the most important, because this inject all functions of pairs } } } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke() is invoked"); String methodName = method.getName(); Object delegate = delegatesByMethod.get(methodName); return method.invoke(delegate, args); } @SuppressWarnings("unchecked") public static Object newInstance(TwoTuple... pairs) { Class[] interfaces = new Class[pairs.length]; for(int i = 0; i < pairs.length; i++) { interfaces[i] = (Class)pairs[i].second;//second represent XXX.class } ClassLoader cl = pairs[0].first.getClass().getClassLoader(); return Proxy.newProxyInstance( cl, interfaces, new MixinProxy(pairs)); } } public class DynamicProxyMixin { public static void main(String[] args) { Object mixin = MixinProxy.newInstance( tuple(new BasicImp(), Basic.class), tuple(new TimeStampedImp(), TimeStamped.class), tuple(new SerialNumberedImp(),SerialNumbered.class)); // Basic b = (Basic)mixin; TimeStamped t = (TimeStamped)mixin; SerialNumbered s = (SerialNumbered)mixin; b.set("Hello"); System.out.println(b.get()); System.out.println(t.getStamp()); System.out.println(s.getSerialNumber()); } } /* get() set() getStamp() getSerialNumber() invoke() is invoked invoke() is invoked Hello invoke() is invoked 1489219456567 invoke() is invoked 1 *///:~
靜態(tài)類型檢查即在程序沒有運(yùn)行時(shí)就能夠通過檢查源代碼確定類型安全蛹疯,與動(dòng)態(tài)類型相對應(yīng)
-
潛在類型機(jī)制即直接可以用模板T戒财,而不用指定該模板屬于哪個(gè)基類,比如在C++里面就可以直接定義
template<class T> void perform(T anything) { anything.speak(); anything.sit(); }
而在java中必須要指明邊界
class Communicate { //must specify the bounds of generic type,but C++ is not necessary public static <T extends Performs> void perform(T performer) { performer.speak(); performer.sit(); } }
-
java對潛在類型機(jī)制的補(bǔ)償?shù)囊环N方式是反射苍苞,如下
class CommunicateReflectively { //接受一個(gè)Object固翰,然后看是哪個(gè)類 public static void perform(Object speaker) { Class<?> spkr = speaker.getClass(); try { try { Method speak = spkr.getMethod("speak"); speak.invoke(speaker); } catch(NoSuchMethodException e) { print(speaker + " cannot speak"); } try { Method sit = spkr.getMethod("sit"); sit.invoke(speaker); } catch(NoSuchMethodException e) { print(speaker + " cannot sit"); } } catch(Exception e) { throw new RuntimeException(speaker.toString(), e); } } }
15.17中15.17.2與15.17.3,15.17.4沒理解
應(yīng)用于序列的泛型技術(shù)很多都會涉及到Iterable<T>接口
數(shù)組
在java中數(shù)組是一種效率最高的存儲和隨機(jī)訪問對象應(yīng)用序列的方式
-
Comparable接口和Comaprator接口用于排序狼纬,jdk中運(yùn)用策略設(shè)計(jì)模式將“保持不變的事物與會發(fā)生改變的事物相分離”羹呵,代碼如下:
//Comparable class Student implements Comparable<Student>{ private String name; private int age; private float score; public Student(String name, int age, float score) { this.name = name; this.age = age; this.score = score; } public String toString() { return name+"\t\t"+age+"\t\t"+score; } @Override public int compareTo(Student o) { // TODO Auto-generated method stub if(this.score>o.score)//score是private的,為什么能夠直接調(diào)用,這是因?yàn)樵赟tudent類內(nèi)部 return -1;//由高到底排序 else if(this.score<o.score) return 1; else{ if(this.age>o.age) return 1;//由底到高排序 else if(this.age<o.age) return -1; else return 0; } } } public class ComparableDemo01 { public static void main(String[] args) { // TODO Auto-generated method stub Student stu[]={new Student("zhangsan",20,90.0f), new Student("lisi",22,90.0f), new Student("wangwu",20,99.0f), new Student("sunliu",22,100.0f)}; java.util.Arrays.sort(stu); for(Student s:stu) { System.out.println(s); } } } //Comparator package edu.sjtu.ist.comutil; import java.util.Comparator; class Student { private String name; private int age; private float score; public Student(String name, int age, float score) { this.name = name; this.age = age; this.score = score; } 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; } public float getScore() { return score; } public void setScore(float score) { this.score = score; } public String toString() { return name+"\t\t"+age+"\t\t"+score; } } class StudentComparator implements Comparator<Student>{ @Override public int compare(Student o1, Student o2) { // TODO Auto-generated method stub if(o1.getScore()>o2.getScore()) return -1; else if(o1.getScore()<o2.getScore()) return 1; else{ if(o1.getAge()>o2.getAge()) return 1; else if(o1.getAge()<o2.getAge()) return -1; else return 0; } } } public class ComparableDemo02 { public static void main(String[] args) { // TODO Auto-generated method stub Student stu[]={new Student("zhangsan",20,90.0f), new Student("lisi",22,90.0f), new Student("wangwu",20,99.0f), new Student("sunliu",22,100.0f)}; java.util.Arrays.sort(stu,new StudentComparator()); for(Student s:stu) { System.out.println(s); } } }
- 當(dāng)你使用最近的java版本編程時(shí)疗琉,應(yīng)該優(yōu)先選擇容器而不是數(shù)組冈欢,只有在證明性能成為問題時(shí),你才應(yīng)該講程序重構(gòu)為使用數(shù)組
容器源碼解讀
- 繼承結(jié)構(gòu)代碼如下:
public interface Iterable<T>{...} public interface Collection<E> extends Iterable<E>{...} public interface Set<E> extends Collection<E>{...} public interface SortedSet<E> extends Set<E>{ Comparator<? super E> comparator(); } public interface List<E> extends Collection<E>{...}
- 抽象類實(shí)現(xiàn)接口盈简,可以不用實(shí)現(xiàn)其全部的方法即可以篩選一些方法來實(shí)現(xiàn)凑耻,比如:
interface test{
void m();
void f();
}
abstract class test2 implements test{
@Override
public void m() {
// TODO Auto-generated method stub
}
}