Jawaban:
Ada perbedaan besar di antara mereka. Di C ++ Anda tidak harus menentukan kelas atau antarmuka untuk tipe generik. Itu sebabnya Anda dapat membuat fungsi dan kelas yang benar-benar generik, dengan peringatan dari pengetikan yang lebih longgar.
template <typename T> T sum(T a, T b) { return a + b; }
Metode di atas menambahkan dua objek dengan tipe yang sama, dan dapat digunakan untuk semua tipe T yang memiliki operator "+".
Di Java Anda harus menentukan tipe jika Anda ingin memanggil metode pada objek yang dilewati, seperti:
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
Dalam C ++ fungsi generik / kelas hanya dapat didefinisikan dalam header, karena kompiler menghasilkan fungsi yang berbeda untuk jenis yang berbeda (yang dipanggil dengan). Jadi kompilasi lebih lambat. Di Jawa kompilasi tidak memiliki penalti besar, tetapi Java menggunakan teknik yang disebut "penghapusan" di mana tipe generik dihapus saat runtime, jadi pada saat runtime Jawa sebenarnya memanggil ...
Something sum(Something a, Something b) { return a.add ( b ); }
Jadi pemrograman generik di Jawa tidak benar-benar berguna, hanya sedikit sintaksis gula untuk membantu dengan konstruksi foreach baru.
EDIT: pendapat di atas tentang kegunaan ditulis oleh diri yang lebih muda. Generik Java membantu tentu saja dengan keamanan jenis.
extends
atau super
. Jawabannya salah,
Java Generics yang secara besar-besaran yang berbeda untuk C ++ template.
Pada dasarnya di C ++ templat pada dasarnya adalah set preprocessor / makro yang dimuliakan ( Catatan: karena beberapa orang tampaknya tidak dapat memahami analogi, saya tidak mengatakan pemrosesan template adalah makro). Di Jawa mereka pada dasarnya sintaksis gula untuk meminimalkan pengecoran boilerplate dari Obyek. Berikut ini adalah pengantar yang cukup baik untuk template C ++ vs Java generics .
Untuk menguraikan hal ini: ketika Anda menggunakan template C ++, Anda pada dasarnya membuat salinan kode lainnya, sama seperti jika Anda menggunakan #define
makro. Ini memungkinkan Anda untuk melakukan hal-hal seperti memiliki int
parameter dalam definisi templat yang menentukan ukuran array dan semacamnya.
Java tidak berfungsi seperti itu. Di Jawa semua objek diperluas dari java.lang.Object jadi, pra-Generik, Anda akan menulis kode seperti ini:
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
karena semua tipe koleksi Java menggunakan Object sebagai tipe dasar mereka sehingga Anda bisa memasukkan apa pun di dalamnya. Java 5 berguling-guling dan menambahkan obat generik sehingga Anda dapat melakukan hal-hal seperti:
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
Dan itu semua Java Generics adalah: pembungkus untuk benda casting. Itu karena Java Generics tidak disempurnakan. Mereka menggunakan tipe erasure. Keputusan ini dibuat karena Java Generics datang sangat terlambat sehingga mereka tidak ingin merusak kompatibilitas (a Map<String, String>
dapat digunakan kapan saja diperlukan Map
). Bandingkan ini dengan .Net / C # di mana type erasure tidak digunakan, yang mengarah ke semua jenis perbedaan (misalnya Anda dapat menggunakan tipe primitif dan IEnumerable
dan IEnumerable<T>
tidak memiliki hubungan satu sama lain).
Dan kelas yang menggunakan generik yang dikompilasi dengan kompiler Java 5+ dapat digunakan di JDK 1.4 (dengan asumsi ia tidak menggunakan fitur atau kelas lain yang membutuhkan Java 5+).
Itu sebabnya Java Generics disebut gula sintaksis .
Tapi keputusan tentang bagaimana melakukan obat generik ini memiliki efek yang sangat besar sehingga (hebat) FAQ Java Generics telah muncul untuk menjawab banyak, banyak pertanyaan yang orang miliki tentang Java Generics.
Templat C ++ memiliki sejumlah fitur yang tidak dimiliki Java Generics:
Penggunaan argumen tipe primitif.
Sebagai contoh:
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Java tidak mengizinkan penggunaan argumen tipe primitif dalam generik.
Penggunaan argumen tipe default , yang merupakan salah satu fitur yang saya lewatkan di Jawa tetapi ada alasan kompatibilitas belakang untuk ini;
Sebagai contoh:
public class ObservableList<T extends List> {
...
}
Perlu ditekankan bahwa permintaan templat dengan argumen berbeda adalah tipe yang berbeda. Mereka bahkan tidak membagikan anggota statis. Di Jawa, ini bukan masalahnya.
Selain perbedaan dengan obat generik, untuk kelengkapan, berikut ini adalah perbandingan dasar C ++ dan Java (dan yang lainnya ).
Dan saya juga bisa menyarankan Berpikir di Jawa . Sebagai seorang programmer C ++ banyak konsep seperti objek sudah menjadi sifat kedua tetapi ada perbedaan yang halus sehingga dapat bermanfaat untuk memiliki teks pengantar bahkan jika Anda membaca bagian-bagiannya.
Banyak hal yang akan Anda pelajari ketika mempelajari Java adalah semua perpustakaan (keduanya standar - yang ada di JDK - dan tidak standar, yang mencakup hal-hal yang biasa digunakan seperti Spring). Sintaks Java lebih verbose daripada sintaksis C ++ dan tidak memiliki banyak fitur C ++ (mis. Operator overloading, multiple inheritance, mekanisme destructor, dll) tetapi itu tidak secara ketat menjadikannya subset dari C ++ juga.
Map map = new HashMap<String, String>
. Ini berarti Anda dapat menggunakan kode baru pada JVM lama dan itu akan berjalan karena kesamaan dalam bytecode.
C ++ memiliki template. Java memiliki generik, yang terlihat agak seperti template C ++, tetapi mereka sangat, sangat berbeda.
Templat berfungsi, seperti namanya, dengan menyediakan kompiler dengan templat (tunggu ...) yang dapat digunakan untuk menghasilkan kode tipe-aman dengan mengisi parameter templat.
Generik, seperti yang saya mengerti, bekerja sebaliknya: parameter tipe digunakan oleh kompiler untuk memverifikasi bahwa kode yang menggunakannya adalah tipe-aman, tetapi kode yang dihasilkan dihasilkan tanpa tipe sama sekali.
Pikirkan template C ++ sebagai sistem makro yang sangat bagus , dan Java generics sebagai alat untuk menghasilkan typecast secara otomatis.
const
. Objek dalam C ++ tidak akan dimodifikasi melalui const
pointer kecuali the const
-ness dibuang. Demikian juga, gips implisit yang dibuat oleh tipe generik di Java dijamin "aman" kecuali parameter tipe secara manual dibuang di suatu tempat dalam kode.
Fitur lain yang dimiliki templat C ++ yang tidak dimiliki generik Java adalah spesialisasi. Itu memungkinkan Anda untuk memiliki implementasi yang berbeda untuk tipe tertentu. Jadi, Anda dapat, misalnya, memiliki versi yang sangat optimal untuk int , sementara masih memiliki versi generik untuk jenis lainnya. Atau Anda dapat memiliki versi berbeda untuk tipe pointer dan non-pointer. Ini sangat berguna jika Anda ingin mengoperasikan objek dereferenced ketika menyerahkan pointer.
Ada penjelasan yang bagus tentang topik ini dalam Java Generics and Collections Oleh Maurice Naftalin, Philip Wadler. Saya sangat merekomendasikan buku ini. Kutipan:
Generik di Jawa menyerupai templat di C ++. ... Sintaksnya sengaja sama dan semantiknya sengaja berbeda. ... Secara semantik, generik Java didefinisikan oleh penghapusan, sedangkan templat C ++ didefinisikan oleh ekspansi.
Silakan baca penjelasan lengkapnya di sini .
(sumber: oreilly.com )
Pada dasarnya, templat AFAIK, C ++ membuat salinan kode untuk setiap jenis, sedangkan generik Java menggunakan kode yang persis sama.
Ya, Anda dapat mengatakan bahwa template C ++ setara dengan konsep generik Java (meskipun lebih tepat jika dikatakan generik Java setara dengan konsep C ++)
Jika Anda terbiasa dengan mekanisme template C ++, Anda mungkin berpikir bahwa generik serupa, tetapi kesamaannya dangkal. Generik tidak menghasilkan kelas baru untuk setiap spesialisasi, mereka juga tidak mengizinkan "metaprogramming template."
dari: Java Generics
Java (dan C #) generik tampaknya merupakan mekanisme substitusi tipe run-time yang sederhana.
Templat C ++ adalah konstruk waktu kompilasi yang memberi Anda cara untuk memodifikasi bahasa yang sesuai dengan kebutuhan Anda. Mereka sebenarnya bahasa murni fungsional yang dijalankan oleh kompiler selama kompilasi.
Keuntungan lain dari template C ++ adalah spesialisasi.
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Special sum(const Special& a, const Special& b) { return a.plus(b); }
Sekarang, jika Anda memanggil jumlah dengan pointer, metode kedua akan dipanggil, jika Anda memanggil jumlah dengan objek non-pointer metode pertama akan dipanggil, dan jika Anda memanggil sum
dengan Special
objek, metode ketiga akan dipanggil. Saya tidak berpikir ini mungkin dengan Java.
Saya akan meringkasnya dalam satu kalimat: templat membuat tipe baru, generik membatasi tipe yang ada.
@Keith:
Kode itu sebenarnya salah dan terlepas dari gangguan yang lebih kecil ( template
dihilangkan, sintaksisasinya terlihat berbeda), spesialisasi parsial tidak bekerja pada templat fungsi, hanya pada templat kelas. Namun kode akan bekerja tanpa spesialisasi templat parsial, alih-alih menggunakan overloading lama:
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Jawaban di bawah ini dari buku Cracking The Coding Wawancara Solusi untuk Bab 13, yang menurut saya sangat bagus.
Penerapan Java generics berakar pada gagasan "type erasure: 'Teknik ini menghilangkan tipe parameter ketika kode sumber diterjemahkan ke bytecode Java Virtual Machine (JVM). Misalnya, Anda memiliki kode Java di bawah ini:
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
Selama kompilasi, kode ini ditulis ulang menjadi:
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
Penggunaan generik Java tidak banyak berubah tentang kemampuan kami; itu hanya membuat segalanya sedikit lebih cantik. Karena alasan ini, obat generik Java terkadang disebut "gula sintaksis: '.
Ini sangat berbeda dari C ++. Dalam C ++, templat pada dasarnya adalah kumpulan makro yang dimuliakan, dengan kompiler membuat salinan baru dari kode templat untuk setiap jenis. Bukti dari ini adalah fakta bahwa turunan dari MyClass tidak akan berbagi variabel statis dengan MyClass. Namun, contoh MyClass akan berbagi variabel statis.
/*** MyClass.h ***/
template<class T> class MyClass {
public:
static int val;
MyClass(int v) { val v;}
};
/*** MyClass.cpp ***/
template<typename T>
int MyClass<T>::bar;
template class MyClass<Foo>;
template class MyClass<Bar>;
/*** main.cpp ***/
MyClass<Foo> * fool
MyClass<Foo> * foo2
MyClass<Bar> * barl
MyClass<Bar> * bar2
new MyClass<Foo>(10);
new MyClass<Foo>(15);
new MyClass<Bar>(20);
new MyClass<Bar>(35);
int fl fool->val; // will equal 15
int f2 foo2->val; // will equal 15
int bl barl->val; // will equal 35
int b2 bar2->val; // will equal 35
Di Jawa, variabel statis dibagi di seluruh instance MyClass, terlepas dari parameter tipe yang berbeda.
Java generics dan C ++ templates memiliki sejumlah perbedaan lainnya. Ini termasuk:
Template tidak lain adalah sistem makro. Gula sintaksis. Mereka sepenuhnya diperluas sebelum kompilasi aktual (atau, setidaknya, kompiler berperilaku seolah-olah itu adalah kasus).
Contoh:
Katakanlah kita menginginkan dua fungsi. Satu fungsi mengambil dua urutan (daftar, array, vektor, apa saja) angka, dan mengembalikan produk dalam mereka. Fungsi lain membutuhkan panjang, menghasilkan dua urutan panjang itu, meneruskannya ke fungsi pertama, dan mengembalikan hasilnya. Tangkapannya adalah kita mungkin melakukan kesalahan pada fungsi kedua, sehingga kedua fungsi ini tidak memiliki panjang yang sama. Kami membutuhkan kompiler untuk memperingatkan kami dalam hal ini. Tidak ketika program sedang berjalan, tetapi saat dikompilasi.
Di Jawa Anda dapat melakukan sesuatu seperti ini:
import java.io.*;
interface ScalarProduct<A> {
public Integer scalarProduct(A second);
}
class Nil implements ScalarProduct<Nil>{
Nil(){}
public Integer scalarProduct(Nil second) {
return 0;
}
}
class Cons<A implements ScalarProduct<A>> implements ScalarProduct<Cons<A>>{
public Integer value;
public A tail;
Cons(Integer _value, A _tail) {
value = _value;
tail = _tail;
}
public Integer scalarProduct(Cons<A> second){
return value * second.value + tail.scalarProduct(second.tail);
}
}
class _Test{
public static Integer main(Integer n){
return _main(n, 0, new Nil(), new Nil());
}
public static <A implements ScalarProduct<A>>
Integer _main(Integer n, Integer i, A first, A second){
if (n == 0) {
return first.scalarProduct(second);
} else {
return _main(n-1, i+1,
new Cons<A>(2*i+1,first), new Cons<A>(i*i, second));
//the following line won't compile, it produces an error:
//return _main(n-1, i+1, first, new Cons<A>(i*i, second));
}
}
}
public class Test{
public static void main(String [] args){
System.out.print("Enter a number: ");
try {
BufferedReader is =
new BufferedReader(new InputStreamReader(System.in));
String line = is.readLine();
Integer val = Integer.parseInt(line);
System.out.println(_Test.main(val));
} catch (NumberFormatException ex) {
System.err.println("Not a valid number");
} catch (IOException e) {
System.err.println("Unexpected IO ERROR");
}
}
}
Dalam C # Anda dapat menulis hal yang hampir sama. Cobalah untuk menulis ulang di C ++, dan itu tidak akan dikompilasi, mengeluh tentang perluasan templat yang tak terbatas.
Saya ingin mengutip askanydifference di sini:
Perbedaan utama antara C ++ dan Java terletak pada ketergantungannya pada platform. Sementara, C ++ adalah bahasa yang bergantung pada platform, Java adalah bahasa yang bebas platform.
Pernyataan di atas adalah alasan mengapa C ++ mampu memberikan tipe generik yang benar. Sementara Java memang memiliki pemeriksaan ketat dan karenanya mereka tidak mengizinkan penggunaan obat generik seperti yang dilakukan C ++.