Java和C++的泛型程序設(shè)計(jì)有什么不同

這篇文章主要講解了“Java和C++的泛型程序設(shè)計(jì)有什么不同”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Java和C++的泛型程序設(shè)計(jì)有什么不同”吧!

興平網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)公司從2013年開(kāi)始到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。

泛型程序設(shè)計(jì):

1.    泛型類的定義,見(jiàn)如下代碼:

public class Pair<T,U> { public Pair() { first = null; second = null; } public Pair(T first,U second) { this.first = first; this.second = second; }  public T getFirst() { return first; } public U getSecond() { return second; } public void setFirst(T first) { this.first = first; } public void setSecond(U second) { this.second = second; }  private T first; private U second; }

以上代碼中的T,U都是泛型類Pair的類型參數(shù)。以下為C++中模板類的定義方式:

template<typename T,typename U> class Pair { public: Pair(T first,U second): _first(first),_second(second) {} ~Pair() {} public: T getFirst() { return _first; } U getSecond() { return _second; } void setFirst(T frist) { _first = first; } void setSecond(U second) { _second = second; } private: T _first; U _second; }

2.    泛型方法的定義,在Java中泛型方法不一定聲明在泛型類中,可以聲明在普通類中,見(jiàn)如下代碼:

public class MyFirst { public static void main(String[] args) throws Exception {  String[] names = {"john","Q.","Public"}; String middle = ArrayAlgo.<String>getMiddle(names); System.out.println(middle); }  }  class ArrayAlgo { public static <T> T getMiddle(T[] a) { return a[a.length/2]; } }

在以上代碼中可以看出getMiddle方法為靜態(tài)泛型方法,類型變量位于修飾符"public static" 的后面,返回值的前面。調(diào)用的時(shí)候在方法名的前面給出了參數(shù)類型。由于Java的編譯器提供類型推演的功能,既類型參數(shù)可以通過(guò)函數(shù)參數(shù)的類型進(jìn)行推演,因此也可以直接調(diào)用泛型函數(shù),如String middle = ArrayAlgo.getMiddle(names)。如果編譯器無(wú)法通過(guò)函數(shù)參數(shù)的類型推演出類型參數(shù)的實(shí)際類型,這樣將會(huì)導(dǎo)致編譯錯(cuò)誤。在C++中同樣存在模板函數(shù),也同樣存在模板函數(shù)的類型推演,在這一點(diǎn)上主要的差異來(lái)自于函數(shù)聲明的語(yǔ)法,見(jiàn)如下C++代碼:

class ArrayAlgo { public: template<typename T> static T getMiddle(T* a,size_t len) { return a[len/2]; } };  int main() { int v[] = {1,2,3}; int ret = ArrayAlgo::getMiddle(v,3); printf("This value is %d.\n",ret); return 0; }

3.    類型參數(shù)的限定:有些泛型函數(shù)在使用類型參數(shù)變量時(shí),經(jīng)常會(huì)用到該類型的特殊方法,如在進(jìn)行數(shù)組元素比較時(shí),要求數(shù)組中的元素必須是Comparable接口的實(shí)現(xiàn)類,見(jiàn)如下代碼:

public static <T> T min(T[] a) { if (a == null || a.length == 0) return null; T smallest = a[0]; for (int i = 0; i < a.length; ++i) { if (smallest.compareTo(a[i]) > 0) smallest = a[i]; } return smallest; }

在以上代碼中,數(shù)組元素的類型為T,如果該類型并未提供compareTo域方法,將會(huì)導(dǎo)致編譯錯(cuò)誤,如何確保類型參數(shù)確實(shí)提供了compareTo方法呢?如果T是Comparable接口的實(shí)現(xiàn)類,那么該方法一定會(huì)被提供,因此可以通過(guò)Java語(yǔ)法中提供的類型參數(shù)限定的方式來(lái)確保這一點(diǎn)。見(jiàn)如下修訂代碼:

public static <T extends Comparable> T min(T[] a) { if (a == null || a.length == 0) return null; T smallest = a[0]; for (int i = 0; i < a.length; ++i) { if (smallest.compareTo(a[i]) > 0) smallest = a[i]; } return smallest; }

其中的<T extends Comparable>語(yǔ)法保證了類型參數(shù)必須是Comparable接口的實(shí)現(xiàn)類,否則將會(huì)導(dǎo)致編譯錯(cuò)誤。Java中可以支持多接口的限定,之間用&分隔,如<T extends Comparable & Serializable>和之前的例子一樣,盡管同樣都會(huì)導(dǎo)致編譯錯(cuò)誤,但是后者不僅會(huì)產(chǎn)生更為明確的編譯錯(cuò)誤信息,同樣也使使用者能夠更加清晰的看到該方法的使用規(guī)則。在標(biāo)準(zhǔn)C++中并未提供這樣的限定,但是在C++中對(duì)該種方式有另外一種稱謂,叫做"類型綁定",在Boost等開(kāi)源庫(kù)中通過(guò)更復(fù)雜的模板技巧模仿了該功能的實(shí)現(xiàn)。然而,就泛型的該功能而言,C#的支持也是相當(dāng)不錯(cuò)的,可參考C#泛型中的which關(guān)鍵字。
在標(biāo)準(zhǔn)C++中,其模板的實(shí)現(xiàn)較Java而言更為靈活和強(qiáng)大。對(duì)于***個(gè)例子中的代碼,只是要求模參必須提供compareTo方法即可通過(guò)編譯。

template<typename T> static T min(T* a,size_t len) { T smallest = a[0]; for (int i = 0; i < len; ++i) { if (smallest.compareTo(a[i]) > 0) smallest = a[i]; } return smallest; }

注:C++中的模板是在引用時(shí)才編譯的,因此如果在模板類型中出現(xiàn)任何語(yǔ)法錯(cuò)誤,但此時(shí)尚未有任何引用時(shí),編譯器是不會(huì)報(bào)錯(cuò)的。
4.    泛型代碼中的類型擦除:記得在我閱讀Thinking in Java 4th 的時(shí)候,書(shū)中給出了一些比較明確的解釋,為什么Java會(huì)這樣實(shí)現(xiàn)泛型,其最主要的原因是為了考慮向前兼容,也承認(rèn)這樣的實(shí)現(xiàn)方式有著很多的缺陷和弊病,希望Java在今后的版本中予以補(bǔ)足。
簡(jiǎn)單的說(shuō)類型擦除,就是幾乎所有的泛型相關(guān)的行為都是由編譯器通過(guò)暗插各種各樣的代碼,或者是暗自修訂部分代碼的聲明,然后再將修訂后的代碼(基本不再包含泛型信息)生成字節(jié)碼后交給JVM去執(zhí)行,因此可以據(jù)此判斷在JVM中對(duì)我們的泛型類型是一無(wú)所知的。C++也是同樣的道理,只是編譯器完成的工作被定義為類型展開(kāi)或類型實(shí)例化,因此,同樣的模板類,如果實(shí)例化的類型參數(shù)不同,那么用他們聲明出來(lái)的類對(duì)象也同樣不屬于相同類型的對(duì)象,其限制主要表現(xiàn)為,不能通過(guò)缺省copy constructor或者缺省賦值操作符來(lái)完成對(duì)象之間的復(fù)制,除非其中某個(gè)類型實(shí)例化后的對(duì)象專門針對(duì)另外一種類型實(shí)例化后的類型進(jìn)行了copy constructor和賦值等于的重載。

1)    類型擦除:將類型參數(shù)替換為限定類型,如果沒(méi)有限定類型則替換為Object,見(jiàn)如下代碼:

public class Pair<T> { public Pair(T first,T second) { this.first = first; this.second = second; } public T getFirst() { return first; } public T getSecond() { return second; } public void setFirst(T first) { this.first = first; } public void setSecond(T second) { this.second = second; } private T first; private T second; }

由于Pair中的類型參數(shù)T沒(méi)有限定類型,因此類型擦除后將會(huì)變成如下代碼:

public class Pair { public Pair(Object first,Object second) { this.first = first; this.second = second; } public Object getFirst() { return first; } public Object getSecond() { return second; } public void setFirst(Object first) { this.first = first; } public void setSecond(Object second) { this.second = second; } private Object first; private Object second; }

因此盡管在調(diào)用Pair時(shí),傳遞的類型參數(shù)有所不同,如String、Date,但是在類型擦除之后,他們將成為相同的類型。如果類型參數(shù)存在多個(gè)限定類型,則取***個(gè)限定類型作為擦除后的類型參數(shù),見(jiàn)如下代碼:

public class Interval<T extends Comparable & Serializable> implements Serializable { public Interval(T first, T second) { if (first.compareTo(second) <= 0) { lower = first; upper = second; } else { lower = second; uppper = first; } } private T lower; private T upper; }

擦除類型信息后的原始類型如下:

public class Interval implements Serializable { public Interval(Comparable first, Comparable second) { if (first.compareTo(second) <= 0) { lower = first; upper = second; } else { lower = second; uppper = first; } } private Comparable lower; private Comparable upper; }

5.    泛型類向遺留代碼的兼容:由于編譯器自動(dòng)完成了類型信息的擦除,因此在原有調(diào)用原始類型的地方,可以直接傳入等價(jià)的泛型類,只要保證該泛型類在類型擦除后可以符合被調(diào)用函數(shù)參數(shù)的語(yǔ)法要求即可,見(jiàn)如下代碼:

public class TestMain { public static void test(MyClass t) { System.out.println(t.getValue()); }  public static void main(String[] args) { MyClass<Integer> v = new MyClass<Integer>(5); test(v); } } class MyClass<T> { public MyClass(T t) { this.t = t; } public T getValue() { return t;} private T t; }

6.    約束與局限性:

1)    不能使用原始類型作為類型參數(shù),如int、double等,因?yàn)樗麄兒蚈bject之間沒(méi)有直接的繼承關(guān)系,因此在需要時(shí)只能使用包裝類,如Integer、Double分別予以替換,不能這樣的替換確實(shí)也帶來(lái)了效率上的折損,C++中沒(méi)有這樣的限制,因此模板類的增多只會(huì)影響編譯的效率和不會(huì)影響運(yùn)行時(shí)的效率。

2)    運(yùn)行時(shí)的類型查詢只適用于原始類型,即if (a instanceof Pair<String>) 等價(jià)于 if (a instanceof Pair)。

3)    泛型類對(duì)象調(diào)用getClass()方法返回的Class對(duì)象都是擦除類型信息的原始Class類型,因此在做比較時(shí),他們將為真,見(jiàn)如下代碼:

public class TestMain { public static void main(String[] args) { MyClass<Integer> i = new MyClass<Integer>(5); MyClass<Double> d = new MyClass<Double>(5.0); //返回的均為MyClass.Class if (d.getClass() == i.getClass())  System.out.println("Type info will be ignored here"); } } class MyClass<T> { public MyClass(T t) { this.t = t; } public T getValue() { return t;} private T t; } /* 輸入結(jié)果: Type info will be ignored here */

4)    泛型類不能實(shí)現(xiàn)Throwable接口,換言之泛型類不能成為異常類,否則會(huì)導(dǎo)致編譯錯(cuò)誤。

5)    不能聲明參數(shù)化類型的數(shù)組,如Pair<String>[] table = new Pair<String>[10]; 在擦除類型后將會(huì)變?yōu)镻air[] table = new Pair[10]; 因此可以執(zhí)行該轉(zhuǎn)換:Objec[] objarray = table; 由于數(shù)組可以記住元素的類型,如果此時(shí)試圖插入錯(cuò)誤的類型元素,將會(huì)導(dǎo)致異常ArrayStoreException的拋出。C++中沒(méi)有該限制。

6)    不能實(shí)例化泛型類型的變量,如public Pair() { first = new T(); second = new T();},C++中不存在這樣的限制,針對(duì)以上寫法,類型T只要存在缺省的構(gòu)造函數(shù)即可。如果確實(shí)需要實(shí)例化類型參數(shù)的對(duì)象,見(jiàn)如下代碼:

public static <T> Pair<T> makePair(Class<T> c1) { return new Pair<T>(c1.newInstance(),c1.newInstance()); } public static void main(String[] args) { //String.class的類型為Class<String> Pair<String> p = Pair.makePair(String.class); }

這里主要是利用Class類型本身也是泛型類型,可以利用Class<T>的類型參數(shù)推演出Pair<T>中T的類型。同樣的道理帶有類型參數(shù)的數(shù)組對(duì)象也不能直接創(chuàng)建,需要利用Array的反射機(jī)制來(lái)輔助完成,見(jiàn)如下代碼:

public static <T extends Comparable> T[] minmax(T[] a) { T[] mm = (T[])Array.newInstance(a.getClass().getComponentType(),a.length); //do something here based on mm return mm; }

7)    泛型類不能應(yīng)用于靜態(tài)上下文中,見(jiàn)如下代碼:

public class Singleton<T> { public static T getInstance() { //Compilation ERROR return singleInstance; } private T singleInstance; //Compilation ERROR }

因?yàn)檫@樣的寫法在定義Singleton<String>和Singleton<Date>之后,由于類型擦除,將會(huì)生成唯一一個(gè)Singleton原始共享對(duì)象,事實(shí)上這并不是我們所期望的結(jié)果,在C++中沒(méi)有這樣的限制,甚至有的時(shí)候還可以利用這樣的機(jī)制針對(duì)不同類型的對(duì)象作聲明計(jì)數(shù)器用,見(jiàn)如下代碼:

template<typename T> class MyClassCounter { public: MyClassCounter(T t) { _t = t; _counter++; }  operator T() { return _t; }  T* operator->() { return &_t; } int getCount() const { return _counter; } private: T _t; static int _counter; }

8)    泛型類不能同時(shí)繼承或?qū)崿F(xiàn)只是擁有不同參數(shù)類型的同一泛型類,如 public class MyClass implements Comparable<String>, Comparable<Date> {}

7.    泛型類型的繼承規(guī)則:

1)    如果 public class Manager extends Employee {},那么Pair<Employee> pe = new Pair<Manager>()將會(huì)是非常的賦值操作,會(huì)導(dǎo)致編譯錯(cuò)誤。試想如下代碼引發(fā)的運(yùn)行時(shí)問(wèn)題。在C++中這種賦值方式同樣會(huì)導(dǎo)致編譯錯(cuò)誤,因?yàn)樗麄冊(cè)陬愋蛯?shí)例化之后就被視為完全無(wú)關(guān)的兩個(gè)類型。

public void test() { Pair<Manager> manager = new Pair<Manager>(); Pair<Employee> employee = manager; //compilation error employee.setFirst(otherEmployeeButNotManager); //employee的另外一個(gè)子類,但不是Manager。 }

2)    數(shù)組由于在運(yùn)行時(shí)會(huì)記住元素的類型,因此數(shù)組可以完成這樣的賦值,如Manager[] manager = {}; Employee[] employee = manager;如果賦值之后出現(xiàn)錯(cuò)誤的元素賦值將會(huì)引發(fā)ArrayStoreException異常。

3)    泛型類型可以直接賦值給擦除類型后的原始類型,但是同樣也會(huì)出現(xiàn)2)中數(shù)組賦值的問(wèn)題,只是觸發(fā)的異常改為ClassCastException,見(jiàn)如下代碼:

public void test() { Pair<Manager> manager = new Pair<Manager>(); Pair rawType = manager; rawType.setFirst("Hello"); //only compilation warning, but will encounter runtime error. }

4)    如果 public class ArrayList<Manager> extends List<Manager> {}, 那么從ArrayList<Manager>到List<Manager>的賦值是允許的,這一點(diǎn)和普通類型是一致的,該規(guī)則同樣適用于C++。

8.    泛型類型的通配符類型:該泛型特征在標(biāo)準(zhǔn)C++中完全不被支持,C#中存在類似的特征。見(jiàn)以下代碼:

1)    子類型限定:

public class Manager extends Employee {}  public static void printBuddies(Pair<Employee> p) { }  public static void main(String[] args) { printBuddies(new Pair<Employee>()); //legal printBuddies(new Pair<Manager>()); //illegal; }

但是如果將printBuddies改為:void printBuddies(Pair<? extends Employee> p),上例中main函數(shù)將可以通過(guò)編譯。<? extends Employee>的語(yǔ)義為所有Employee的子類都可以做printBuddies函數(shù)參數(shù)的類型參數(shù)。對(duì)于7-1)中的示例代碼,如果改為通配符類型將可以通過(guò)編譯并正常運(yùn)行,但是仍然存在一定的限制,見(jiàn)如下代碼:

public void test() { Pair<Manager> manager = new Pair<Manager>(); Pair<? extends Employee> employee = manager; //legal here. //由于otherEmployeeButNotManager雖為Employee子類,但可能并非Manager類, //由于setFirst的參數(shù)將會(huì)聲明為void setFirst(? extends Employee),由于 //編譯器無(wú)法確定setFirst參數(shù)的實(shí)際類型,因此將會(huì)直接報(bào)告編譯錯(cuò)誤。 employee.setFirst(otherEmployeeButNotManager); //compilation error }

和setFirst相比,getFirst將會(huì)正常編譯并運(yùn)行,因?yàn)榉祷刂禑o(wú)論是什么子類型,都不會(huì)帶來(lái)影響和破壞。

2)    超類型限定:Pair<? super Manager>表示參數(shù)類型一定是Manager的超類。因此和子類型限定剛好相反,setFirst將是合法的,而getFirst將會(huì)產(chǎn)生編譯錯(cuò)誤。

3)    無(wú)限定通配符,如Pair<?>,該泛型類型的setFirst(?)方法不能被調(diào)用,即便傳入的參數(shù)是Object,這樣是Pair<?>和Pair之間***的差異。該方法還有一個(gè)比較重要的作用就是用于提示泛型函數(shù)的調(diào)用者,該泛型函數(shù)更期望參數(shù)是帶有類型參數(shù)的泛型類型,而不是原始類型,即便原始類型也可能正常的工作,見(jiàn)如下代碼:

public class TestMain {  @SuppressWarnings("unchecked") //public static <T> T print(MyClass myclass),同樣可以正常的工作, //但是會(huì)有編譯警告產(chǎn)生。 public static <T> T print(MyClass<?> myclass) { myclass.print(); return (T)myclass.get(); }  public static void main(String[] args) { Integer ii = new Integer(5); print(new MyClass<Integer>(ii)); } }  class MyClass<T> { public MyClass(T t) { _t = t; } T get() { return _t;}  public void print() { System.out.println(_t); } private T _t; }

感謝各位的閱讀,以上就是“Java和C++的泛型程序設(shè)計(jì)有什么不同”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Java和C++的泛型程序設(shè)計(jì)有什么不同這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

本文標(biāo)題:Java和C++的泛型程序設(shè)計(jì)有什么不同
網(wǎng)頁(yè)地址:http://bm7419.com/article16/igchgg.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、做網(wǎng)站、移動(dòng)網(wǎng)站建設(shè)、商城網(wǎng)站、網(wǎng)站設(shè)計(jì)、用戶體驗(yàn)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站優(yōu)化排名