解释型语言和编译型语言的区别_从泛型的使用情况看出你对语言的理解程度(2)...
上篇我們提到:Java中的泛型是不可變的,可以通過<? extends E>實(shí)現(xiàn)了泛型的協(xié)變,<? super E>實(shí)現(xiàn)泛型的逆變。從泛型的使用情況看出你對語言的理解程度(1)
今天我們來講講泛型單例工廠,在之前的推文中也有推送過單例模式的實(shí)現(xiàn),但是不是用泛型實(shí)現(xiàn)的,這次我們先講一個(gè)泛型單例的例子,然后再講泛型單例工廠會更好理解一些。
首先定義一個(gè)泛型接口,里面包含一個(gè)apply方法:
public interface Money<T> { T apply(T args); }然后我們需要一種String類型的數(shù)據(jù)進(jìn)行apply操作的單例對象。按慣性思維,我們會這么實(shí)現(xiàn)。
public class PaperMoney implements Money<String>{private static PaperMoney paperInstance = new PaperMoney();private PaperMoney(){} public PaperMoney getInstance() {return paperInstance;}@Overridepublic String apply(String args) { return args;} }在工程需求復(fù)雜的情況下,這種模式?jīng)]有考慮到代碼的擴(kuò)展性,當(dāng)未來需要對Integer對象數(shù)據(jù)進(jìn)行apply操作呢?再寫一個(gè)類嗎?這明顯是過于麻煩了。這時(shí)候就需要再結(jié)合工廠的設(shè)計(jì)模式了。
泛型單例工廠
在工廠中,會產(chǎn)生不同參數(shù)類型的Money對象,在結(jié)合static的特性,實(shí)現(xiàn)復(fù)用生成的Money對象。
public class MoneyImp {//Money是類似函數(shù)式接口實(shí)現(xiàn)private static Money<Object> IDENTITY_FUNCTION = arg -> String.valueOf(arg.hashCode());@SuppressWarnings("unchecked")public static <T> Money<T> getMoneyInstance() {return (Money<T>) IDENTITY_FUNCTION;}public static void main(String[] args) {String[] strings = { "one", "five", "ten" };Money<String> paperMoney = getMoneyInstance();for (String s : strings) {System.out.println(paperMoney.apply(s));}Integer[] numbers = { 1, 2, 3 };Money<Integer> coinMoney = getMoneyInstance();for (Integer n : numbers)System.out.println(coinMoney.apply(n));JSONObject[] jsonObjects = {JSON.parseObject("{hah:1}")};Money<JSONObject> objMoney = getMoneyInstance();for (JSONObject n : jsonObjects)System.out.println(objMoney.apply(n));} }上面的代碼中Money接口其實(shí)是仿照J(rèn)ava8中的Function函數(shù)式接口定義的,或者說是仿照Function接口的子類接口:UnaryOperator。關(guān)于Function函數(shù)式接口可以看今天的另一篇推文介紹。簡單的說,Function就是實(shí)現(xiàn)了這樣的一個(gè)公式:y=f(x),而UnaryOperator實(shí)現(xiàn)的是:x=f(x)。
也就是說IDENTITY_FUNCTION 其實(shí)實(shí)現(xiàn)的是x=String.valueOf(f(x))的功能。在這里我們將傳進(jìn)來的x獲取其hashCode,然后轉(zhuǎn)換成字符串形式返回回去。同時(shí)由于IDENTITY_FUNCTION 是一個(gè)Money<Object> 。用Object接收返回的String(f(x))所以是合理的(父類引用指向子類對象)所以,還是可以把IDENTITY_FUNCTION 看作是x=f(x)。
arg -> String.valueOf(arg.hashCode());這個(gè)函數(shù)由于hashCode() 屬于Object,所以任何類調(diào)用都不會報(bào)錯(cuò)。否則很容易報(bào)錯(cuò),這里要多注意。
如果沒有g(shù)etMoneyInstance() 方法,而是直接把IDENTITY_FUNCTION 賦值給paperMoney 即:
Money<String> paperMoney = IDENTITY_FUNCTION();
則會報(bào)編譯錯(cuò)誤:
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stringat com.ltjh.imports.job.MoneyImp.main(MoneyImp.java:27)這個(gè)很明顯,因?yàn)榉盒褪遣豢梢詤f(xié)變的。所以我們需要一個(gè)靜態(tài)工廠方法:getMoneyInstance()。
所有重點(diǎn)來了:
getMoneyInstance() 方法的作用,則是作為一個(gè)靜態(tài)工廠方法用于獲取我們編寫的IDENTITY_FUNCTION 函數(shù)。代碼中由于對IDENTITY_FUNCTION 進(jìn)行了強(qiáng)制類型轉(zhuǎn)換return (Money<T>) IDENTITY_FUNCTION; 所以需要添加unchecked 轉(zhuǎn)換警告,因?yàn)榫幾g器并不知道Money<Object> 的每個(gè)都是Money<T> ,但是因?yàn)镮DENTITY_FUNCTION 表示的是x=f(x),所以我們可以確定返回的就是Money<T> 。這是類型安全的。一旦我們這樣做了,代碼編譯就不會出現(xiàn)錯(cuò)誤或警告。
于是,我們就可通過getMoneyInstance() 獲取到處理特定類型的Money 如:paperMoney 、coinMoney 和 objMoney 。也就實(shí)現(xiàn)了一個(gè)泛型的單例工廠。
上面代碼的輸出結(jié)果如下:
110182 3143346 114717 1 2 3 103054《Effective Java》中對泛型單例工廠的描述如下:
有時(shí),你需要創(chuàng)建一個(gè)對象,該對象是不可變的,但適用于許多不同類型。因?yàn)榉盒褪怯刹脸龑?shí)現(xiàn)的,所以你可以為所有需要的類型參數(shù)化使用單個(gè)對象,但是你需要編寫一個(gè)靜態(tài)工廠方法,為每個(gè)請求的類型參數(shù)化重復(fù)分配對象。這種模式稱為泛型單例工廠,可用于函數(shù)對象,如 Collections.reverseOrder,偶爾也用于集合,如 Collections.emptySet。
最后再提一點(diǎn)關(guān)于擦除的,由于Java泛型是由擦除實(shí)現(xiàn)的,所以,其實(shí)上面的代碼在便后后類似于這樣:
public class MoneyImp {//Money是類似函數(shù)式接口實(shí)現(xiàn)private static Money IDENTITY_FUNCTION = arg -> String.valueOf(arg.hashCode());@SuppressWarnings("unchecked")public static Money getMoneyInstance() {return IDENTITY_FUNCTION;}public static void main(String[] args) {String[] strings = { "one", "five", "ten" };Money paperMoney = getMoneyInstance();for (String s : strings) {System.out.println(paperMoney.apply(s));}Integer[] numbers = { 1, 2, 3 };Money coinMoney = getMoneyInstance();for (Integer n : numbers)System.out.println(coinMoney.apply(n));JSONObject[] jsonObjects = {JSON.parseObject("{hah:1}")};Money objMoney = getMoneyInstance();for (JSONObject n : jsonObjects)System.out.println(objMoney.apply(n));} }運(yùn)行結(jié)果和前面的一樣且不報(bào)錯(cuò)。那我們?yōu)槭裁催€這么費(fèi)勁用個(gè)泛型搞得云里霧里的呢?因?yàn)槲覀兿胍@取到專門處理某一種類型的Money:paperMoney 、coinMoney 、objMoney 。如果不適用泛型,用上面的代碼,那么這三個(gè)paperMoney 、coinMoney 、objMoney 其實(shí)是沒有限制的,沒辦法裝門處理某一種特定類型啦。
好了,就到這里,不理解的朋友可以留言一起討論哦~
關(guān)注公眾號獲取更多內(nèi)容,有問題也可在公眾號提問哦
強(qiáng)哥叨逼叨
叨逼叨編程、互聯(lián)網(wǎng)的見解和新鮮事
總結(jié)
以上是生活随笔為你收集整理的解释型语言和编译型语言的区别_从泛型的使用情况看出你对语言的理解程度(2)...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: php fuzzy,模糊C均值聚类算法(
- 下一篇: pytorch默认初始化_Pytorch