Yanıtlar:
Aralarında büyük bir fark var. C ++ 'da, genel tip için bir sınıf veya arabirim belirtmeniz gerekmez. Bu nedenle, daha gevşek bir yazımın uyarısı ile gerçekten genel fonksiyonlar ve sınıflar oluşturabilirsiniz.
template <typename T> T sum(T a, T b) { return a + b; }
Yukarıdaki yöntem aynı türden iki nesne ekler ve "+" operatörüne sahip herhangi bir T türü için kullanılabilir.
Java'da, geçirilen nesneler üzerindeki yöntemleri çağırmak istiyorsanız bir tür belirtmeniz gerekir:
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
C ++ 'da genel işlevler / sınıflar yalnızca üstbilgilerde tanımlanabilir, çünkü derleyici farklı türler için farklı işlevler üretir (çağrıldığı gibi). Yani derleme daha yavaş. Java'da derlemenin büyük bir cezası yoktur, ancak Java, genel türün çalışma zamanında silindiği "silme" adlı bir teknik kullanır, bu nedenle çalışma zamanında Java gerçekten çağırır ...
Something sum(Something a, Something b) { return a.add ( b ); }
Java'da genel programlama gerçekten kullanışlı değil, yeni foreach yapısına yardımcı olmak için sadece biraz sözdizimsel şeker.
DÜZENLEME: yukarıdaki kullanışlılık hakkındaki görüş genç bir ben tarafından yazılmıştır. Java'nın jenerikleri elbette tip güvenliğine yardımcı olur.
extends
ya super
. Cevap yanlış,
Java Generics, C ++ şablonlarından çok farklıdır.
Temelde C ++ şablonlarında temelde yüceltilmiş bir önişlemci / makro seti vardır ( Not: bazı insanlar benzetmeyi anlayamıyor gibi göründüğünden, şablon işlemenin bir makro olduğunu söylemiyorum). Java'da temel olarak Nesnelerin kazan plakası dökümünü en aza indirmek için sözdizimsel şekerdir. İşte Java generics vs C ++ şablonlarına oldukça iyi bir giriş .
Bu noktayı ele almak için: bir C ++ şablonu kullandığınızda, tıpkı bir #define
makro kullanıyormuşsunuz gibi, kodun başka bir kopyasını oluşturursunuz . Bu, int
şablon tanımlarında dizilerin boyutlarını belirleyen parametreler ve benzeri şeyler yapmanıza olanak tanır .
Java böyle çalışmaz. Java'da tüm nesneler java.lang.Object'ten uzanır, Generics öncesi, şöyle bir kod yazarsınız:
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
çünkü tüm Java toplama türleri Object'i temel türü olarak kullandı, böylece içine bir şey koyabilirsiniz. Java 5 yuvarlanır ve jenerikler ekler, böylece aşağıdakileri yapabilirsiniz:
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
Ve işte hepsi Java Generics: nesneleri dökmek için sarıcılar. Java Generics rafine edilmemiş olmasıdır. Tip silme kullanırlar. Bu karar, Java Generics'in geriye dönük uyumluluğu kırmak istemedikleri parçada çok geç gelmesi nedeniyle verildi (a Map<String, String>
istendiğinde kullanılabilir Map
). Tip silme kullanılmadığı durumlarda, .Net / C # ile karşılaştırın hangi farklılıklara her türlü (örneğin siz ve ilkel türlerini kullanabilirsiniz için potansiyel müşteriler IEnumerable
ve IEnumerable<T>
birbirlerine hiçbir ilişkisi olmayan).
Ve Java 5+ derleyicisiyle derlenen jenerikleri kullanan bir sınıf JDK 1.4'te kullanılabilir (Java 5+ gerektiren başka özellikler veya sınıflar kullanmadığı varsayılarak).
Bu yüzden Java Generics sözdizimsel şeker olarak adlandırılır .
Ancak jeneriklerin nasıl yapılacağına dair bu karar çok derin etkilere sahiptir, böylece (mükemmel) Java Generics SSS , insanların Java Generics ile ilgili birçok soruya cevap vermek için ortaya çıktı.
C ++ şablonlarında Java Generics'in kullanmadığı birçok özellik vardır:
İlkel tür argümanlarının kullanımı.
Örneğin:
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Java, jeneriklerde ilkel tür argümanlarının kullanılmasına izin vermez.
Kullanımı varsayılan tür argümanları Java bir özellik ben özledim ama bunun için uyumluluk nedenleri geriye vardır;
Örneğin:
public class ObservableList<T extends List> {
...
}
Farklı argümanlara sahip şablon çağrılarının gerçekten farklı türler olduğunu gerçekten vurgulamak gerekir. Statik üyeleri paylaşmıyorlar bile. Java'da durum böyle değildir.
Jeneriklerle olan farklılıkların yanı sıra, bütünlük için, burada C ++ ve Java'nın (ve başka birinin ) temel bir karşılaştırması .
Java'da Düşünmeyi de önerebilirim . Bir C ++ programcısı olarak nesneler gibi kavramların birçoğu zaten ikinci doğa olacaktır, ancak ince farklılıklar vardır, bu yüzden parçaları yağlasanız bile bir giriş metnine sahip olmak faydalı olabilir.
Java öğrenirken öğreneceğiniz birçok şey tüm kütüphanelerdir (her ikisi de standart - JDK'da gelenler - ve Bahar gibi yaygın olarak kullanılan şeyleri içeren standart olmayan). Java sözdizimi, C ++ sözdiziminden daha ayrıntılıdır ve çok fazla C ++ özelliğine (örn. Operatör aşırı yüklenmesi, çoklu kalıtım, yıkıcı mekanizma, vb.) Sahip değildir, ancak bu kesinlikle C ++ 'ın bir alt kümesi değildir.
Map map = new HashMap<String, String>
. Bu, eski bir JVM'ye yeni kod dağıtabileceğiniz anlamına gelir ve bayt kodundaki benzerlikler nedeniyle çalışır.
C ++ şablonları vardır. Java, C ++ şablonları gibi biraz sorta benzeyen jeneriklere sahiptir, ancak çok, çok farklıdırlar.
Şablonlar, adından da anlaşılacağı gibi, derleyiciye şablon parametrelerini doldurarak tür güvenli kod oluşturmak için kullanabileceği bir (bekle ...) şablonu sağlayarak çalışır.
Jenerikler, anladığım kadarıyla, başka bir şekilde çalışın: tür parametreleri, bunları kullanan kodun tipte güvenli olduğunu doğrulamak için derleyici tarafından kullanılır, ancak sonuçta ortaya çıkan kod hiç tür olmadan oluşturulur.
C ++ şablonlarını gerçekten iyi bir makro sistemi ve Java jeneriklerini otomatik olarak daktilolar oluşturmak için bir araç olarak düşünün .
const
. -++ atmadığı const
sürece C ++ 'ta bir nesne bir işaretçi ile değiştirilmez const
. Benzer şekilde, Java'da genel türler tarafından oluşturulan örtük yayınların, tür parametreleri kodun herhangi bir yerine manuel olarak dökülmediği sürece "güvenli" olacağı garanti edilir.
C ++ şablonlarının Java jeneriklerinin sahip olmadığı bir diğer özellik uzmanlıktır. Bu, belirli türler için farklı bir uygulamaya sahip olmanızı sağlar. Böylece, örneğin, türlerin geri kalanı için genel bir sürüme sahipken, bir int için oldukça optimize edilmiş bir sürüme sahip olabilirsiniz. Veya işaretçi ve işaretçi olmayan türler için farklı sürümleriniz olabilir. Bir işaretçi teslim edildiğinde kayıttan çıkarılmış nesne üzerinde çalışmak istiyorsanız bu kullanışlı olur.
Java Generics and Collections'ta bu konuyla ilgili harika bir açıklama By Maurice Naftalin, Philip Wadler. Bu kitabı tavsiye ederim. Alıntılamak:
Java'daki jenerikler C ++ şablonlarına benzer. ... Sözdizimi kasıtlı olarak benzer ve anlambilim kasten farklıdır. ... Anlamsal olarak, Java jenerikleri silme ile tanımlanır, burada C ++ şablonları genişleme ile tanımlanır.
Lütfen tüm açıklamayı buradan okuyun .
(kaynak: oreilly.com )
Temel olarak, AFAIK, C ++ şablonları her tür için kodun bir kopyasını oluştururken Java jenerikleri tam olarak aynı kodu kullanır.
Evet, C ++ şablonunun Java genel kavramına eşdeğer olduğunu söyleyebilirsiniz (daha doğru bir şekilde Java jeneriklerinin kavramdaki C ++ ile eşdeğer olduğunu söylemek gerekir)
C ++ 'ın şablon mekanizmasını biliyorsanız, jeneriklerin benzer olduğunu düşünebilirsiniz, ancak benzerlik yüzeyseldir. Jenerikler her uzmanlık için yeni bir sınıf oluşturmaz ve “şablon meta programlamasına” izin vermez.
from: Java Generics
Java (ve C #) jenerikleri, basit bir çalışma zamanı türü değiştirme mekanizması gibi görünmektedir.
C ++ şablonları, dili ihtiyaçlarınıza göre değiştirmenin bir yolunu sunan bir derleme zamanı yapısıdır. Aslında derleyicinin bir derleme sırasında yürüttüğü tamamen işlevsel bir dildir.
C ++ şablonlarının bir diğer avantajı uzmanlaşmadır.
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); }
Şimdi işaretçileri ile toplamını ararsanız olmayan pointer ile toplamını ararsanız, ikinci yöntem ilk yöntem çağrılır, nesnelerin adı verilecek ve siz ararsanız sum
ile Special
nesneler, üçüncü çağrılacak. Bunun Java ile mümkün olduğunu düşünmüyorum.
Tek bir cümleyle özetleyeceğim: şablonlar yeni türler oluşturur, jenerikler mevcut türleri kısıtlar.
@Keith:
Bu kod aslında yanlış ve birbirinden küçük glitches (dan template
atlanmış, uzmanlaşma sözdizimi farklı görünüyor), kısmi uzmanlaşma değil yalnızca sınıf şablonlara, fonksiyon şablonları üzerinde çalışmaya. Bununla birlikte, kod kısmi şablon uzmanlığı olmadan çalışır, bunun yerine düz eski aşırı yükleme kullanılır:
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Aşağıdaki cevap, Kodlama Röportaj Çözümlerini Kırma kitabının Bölüm 13'e kadar olduğunu düşünüyorum, ki bence çok iyi.
Java jeneriklerinin uygulanması "tip silme:" fikrinden kaynaklanır: Bu teknik, kaynak kodu Java Sanal Makinesi (JVM) bayt koduna çevrildiğinde parametrelenmiş türleri ortadan kaldırır. Örneğin, aşağıdaki Java koduna sahip olduğunuzu varsayalım:
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
Derleme sırasında, bu kod yeniden yazılır:
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
Java jeneriklerinin kullanımı yeteneklerimiz hakkında pek bir şey değiştirmedi; işleri biraz daha güzelleştirdi. Bu nedenle Java jeneriklerine bazen "sözdizimsel şeker:" denir.
Bu C ++ 'dan oldukça farklıdır. C ++ 'da, şablonlar esasen yüceltilmiş bir makro kümesidir ve derleyici her tür için şablon kodunun yeni bir kopyasını oluşturur. Bunun kanıtı, MyClass örneğinin statik bir değişkeni MyClass ile paylaşmayacağı gerçeğidir. Ancak, MyClass'ın yedek örnekleri statik bir değişkeni paylaşacaktır.
/*** 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
Java'da, statik değişkenler, farklı tür parametrelerinden bağımsız olarak MyClass örnekleri arasında paylaşılır.
Java jenerikleri ve C ++ şablonlarının bir dizi başka farkı vardır. Bunlar:
Şablonlar bir makro sistemden başka bir şey değildir. Sözdizimi şekeri. Gerçek derlemeden önce tamamen genişletilirler (veya en azından derleyiciler durumdaymış gibi davranırlar).
Misal:
Diyelim ki iki işlev istiyoruz. Bir işlev iki dizi (liste, diziler, vektörler, ne olursa olsun) sayıları alır ve iç çarpımlarını döndürür. Başka bir işlev uzunluk alır, bu uzunluktan iki dizi oluşturur, onları ilk işleve geçirir ve sonucunu döndürür. Yakalama, ikinci fonksiyonda bir hata yapabiliriz, böylece bu iki fonksiyon gerçekten aynı uzunlukta değildir. Bu durumda bizi uyarması için derleyiciye ihtiyacımız var. Program çalışırken değil, derlenirken.
Java'da böyle bir şey yapabilirsiniz:
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");
}
}
}
C # 'da neredeyse aynı şeyi yazabilirsiniz. C ++ 'da yeniden yazmaya çalışın ve şablonların sonsuz genişlemesinden şikayet ederek derlenmeyecek.
Burada askanydifference alıntı yapmak istiyorum:
C ++ ve Java arasındaki temel fark, platforma bağımlı olmalarında yatmaktadır. C ++ platforma bağımlı bir dilken, Java platformdan bağımsız bir dildir.
Yukarıdaki ifade, C ++ 'ın gerçek genel türler sağlayabilmesinin nedenidir. Java sıkı denetime sahip olsa da, bu nedenle jeneriklerin C ++ 'ın izin verdiği şekilde kullanılmasına izin vermezler.