Domanda

Java ha dei generici e C++ fornisce un modello di programmazione molto forte templateS.Allora, qual è la differenza tra i generici C++ e Java?

È stato utile?

Soluzione

C'è una grande differenza tra loro.In C++ non è necessario specificare una classe o un'interfaccia per il tipo generico.Ecco perché puoi creare funzioni e classi veramente generiche, con l'avvertenza di una digitazione più flessibile.

template <typename T> T sum(T a, T b) { return a + b; }

Il metodo sopra aggiunge due oggetti dello stesso tipo e può essere utilizzato per qualsiasi tipo T che abbia l'operatore "+" disponibile.

In Java devi specificare un tipo se vuoi chiamare metodi sugli oggetti passati, qualcosa del tipo:

<T extends Something> T sum(T a, T b) { return a.add ( b ); }

In C++ funzioni/classi generiche possono essere definite solo nelle intestazioni, poiché il compilatore genera funzioni diverse per tipi diversi (con cui viene invocato).Quindi la compilazione è più lenta.In Java la compilazione non presenta penalità importanti, ma Java utilizza una tecnica chiamata "cancellazione" in cui il tipo generico viene cancellato in fase di runtime, quindi in fase di runtime Java in realtà chiama ...

Something sum(Something a, Something b) { return a.add ( b ); }

Quindi la programmazione generica in Java non è realmente utile, è solo un po' di zucchero sintattico per aiutare con il nuovo costrutto foreach.

MODIFICARE: l'opinione di cui sopra sull'utilità è stata scritta da un me stesso più giovane.I generici di Java aiutano ovviamente con la sicurezza dei tipi.

Altri suggerimenti

I generici Java lo sono in modo massiccio diverso dai modelli C++.

Fondamentalmente in C++ i modelli sono fondamentalmente un glorificato set di preprocessore/macro (Nota: poiché alcune persone sembrano incapaci di comprendere un'analogia, non sto dicendo che l'elaborazione del modello sia una macro).In Java sono fondamentalmente zucchero sintattico per ridurre al minimo il casting di oggetti.Eccone uno abbastanza decente introduzione ai modelli C++ e ai generici Java.

Per approfondire questo punto:quando usi un modello C++, stai praticamente creando un'altra copia del codice, proprio come se usassi un modello #define macro.Ciò ti consente di fare cose come have int parametri nelle definizioni dei modelli che determinano le dimensioni degli array e simili.

Java non funziona così.In Java tutti gli oggetti si estendono da java.lang.Object quindi, prima di Generics, scriveresti un codice come questo:

public class PhoneNumbers {
  private Map phoneNumbers = new HashMap();

  public String getPhoneNumber(String name) {
    return (String)phoneNumbers.get(name);
  }

  ...
}

perché tutti i tipi di raccolta Java utilizzavano Object come tipo base in modo da poter inserire qualsiasi cosa al loro interno.Java 5 si avvia e aggiunge generici in modo da poter fare cose come:

public class PhoneNumbers {
  private Map<String, String> phoneNumbers = new HashMap<String, String>();

  public String getPhoneNumber(String name) {
    return phoneNumbers.get(name);
  }

  ...
}

E questo è tutto ciò che Java Generics è:involucri per la fusione di oggetti.Questo perché i generici Java non sono raffinati.Usano la cancellazione dei caratteri.Questa decisione è stata presa perché Java Generics è arrivato così tardi nel pezzo che non volevano interrompere la compatibilità con le versioni precedenti (un Map<String, String> è utilizzabile ogni volta che a Map è richiesto).Confrontalo con .Net/C# dove non viene utilizzata la cancellazione del tipo, il che porta a tutti i tipi di differenze (ad es.puoi usare tipi primitivi e IEnumerable E IEnumerable<T> non hanno alcuna relazione tra loro).

E una classe che utilizza generici compilati con un compilatore Java 5+ è utilizzabile su JDK 1.4 (supponendo che non utilizzi altre funzionalità o classi che richiedono Java 5+).

Ecco perché vengono chiamati Java Generics zucchero sintattico.

Ma questa decisione su come produrre i generici ha effetti così profondi che il (superbo) Domande frequenti sui generici Java è nato per rispondere alle tante, tantissime domande che le persone hanno su Java Generics.

I modelli C++ hanno una serie di funzionalità che Java Generics non hanno:

  • Utilizzo di argomenti di tipo primitivo.

    Per esempio:

    template<class T, int i>
    class Matrix {
      int T[i][i];
      ...
    }
    

    Java non consente l'uso di argomenti di tipo primitivo nei generici.

  • Utilizzo di argomenti di tipo predefinito, che è una caratteristica che mi manca in Java ma ci sono ragioni di compatibilità con le versioni precedenti per questo;

  • Java consente il limite degli argomenti.

Per esempio:

public class ObservableList<T extends List> {
  ...
}

È davvero necessario sottolineare che le invocazioni di template con argomenti diversi sono in realtà di tipi diversi.Non condividono nemmeno membri statici.In Java non è così.

A parte le differenze con i generici, per completezza, ecco a confronto di base tra C++ e Java (E un altro).

E posso anche suggerire Pensare in Java.Come programmatore C++, molti concetti come gli oggetti saranno già una seconda natura, ma ci sono sottili differenze, quindi può essere utile avere un testo introduttivo anche se si scremano le parti.

Molto di ciò che imparerai imparando Java sono tutte le librerie (sia standard - ciò che è disponibile in JDK - sia non standard, che include cose di uso comune come Spring).La sintassi Java è più dettagliata della sintassi C++ e non ha molte funzionalità C++ (ad es.sovraccarico degli operatori, ereditarietà multipla, meccanismo del distruttore, ecc.) ma ciò non lo rende nemmeno strettamente un sottoinsieme di C++.

C++ ha modelli.Java ha dei generici, che assomigliano un po' ai modelli C++, ma sono molto, molto diversi.

I modelli funzionano, come suggerisce il nome, fornendo al compilatore un modello (aspettalo...) che può utilizzare per generare codice indipendente dai tipi compilando i parametri del modello.

I generici, a quanto ho capito, funzionano al contrario:i parametri di tipo vengono usati dal compilatore per verificare che il codice che li utilizza sia indipendente dai tipi, ma il codice risultante viene generato senza tipi.

Pensa ai modelli C++ come a veramente buono sistema macro e generici Java come strumento per generare automaticamente typecast.

 

Un'altra caratteristica dei modelli C++ che i generici Java non hanno è la specializzazione.Ciò consente di avere un'implementazione diversa per tipi specifici.Quindi puoi, ad esempio, avere una versione altamente ottimizzata per un int, pur avendo una versione generica per il resto dei tipi.Oppure puoi avere versioni diverse per i tipi puntatore e non puntatore.Ciò è utile se si desidera operare sull'oggetto dereferenziato quando si passa un puntatore.

C'è un'ottima spiegazione di questo argomento in Generici e raccolte JavaDi Maurice Naftalin, Philip Wadler.Consiglio vivamente questo libro.Per citare:

I generici in Java assomigliano ai modelli in C ++....La sintassi è deliberatamente simile e la semantica è deliberatamente diversa....Semanticamente, i generici Java sono definiti per cancellazione, dove come i modelli C ++ sono definiti dall'espansione.

Si prega di leggere la spiegazione completa Qui.

alt text
(fonte: oreilly.com)

Fondamentalmente, per quanto ne so, i modelli C++ creano una copia del codice per ogni tipo, mentre i generici Java utilizzano esattamente lo stesso codice.

Si tu poter dire quel modello C++ è equivalente a Java generico concetto (anche se più propriamente sarebbe dire che i generici Java sono equivalenti a C++ concettualmente)

Se hai familiarità con il meccanismo dei modelli di C++, potresti pensare che i generici siano simili, ma la somiglianza è superficiale.I generici non generano una nuova classe per ogni specializzazione, né consentono la “metaprogrammazione dei modelli”.

da: Generici Java

I generici Java (e C#) sembrano essere un semplice meccanismo di sostituzione del tipo in fase di esecuzione.
I modelli C++ sono un costrutto in fase di compilazione che ti offre la possibilità di modificare il linguaggio in base alle tue esigenze.In realtà sono un linguaggio puramente funzionale che il compilatore esegue durante una compilazione.

Un altro vantaggio dei modelli C++ è la specializzazione.

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); }

Ora, se chiami sum con puntatori, verrà chiamato il secondo metodo, se chiami sum con oggetti non puntatori verrà chiamato il primo metodo e se chiami sum sum con Special oggetti, verrà chiamato il terzo.Non penso che questo sia possibile con Java.

Lo riassumerò in una sola frase:i modelli creano nuovi tipi, i generici limitano i tipi esistenti.

@Keith:

Quel codice è in realtà sbagliato e, a parte i piccoli problemi (template omesso, la sintassi della specializzazione ha un aspetto diverso), specializzazione parziale no lavorare sui modelli di funzioni, solo sui modelli di classe.Il codice funzionerebbe tuttavia senza una specializzazione parziale del modello, utilizzando invece il semplice vecchio sovraccarico:

template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }

La risposta che segue è tratta dal libro Craccare l'intervista sulla codifica Soluzioni al capitolo 13, che penso sia molto buono.

L'implementazione dei generici Java è radicata nell'idea di "cancellazione del tipo": questa tecnica elimina i tipi parametrizzati quando il codice sorgente viene tradotto nel bytecode Java Virtual Machine (JVM).Ad esempio, supponiamo di avere il codice Java seguente:

Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);

Durante la compilazione, questo codice viene riscritto in:

Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);

L'uso dei generici Java non ha cambiato molto le nostre capacità;ha solo reso le cose un po' più belle.Per questo motivo i generici Java vengono talvolta chiamati"zucchero sintattico:".

Questo è abbastanza diverso dal C++.In C++, i modelli sono essenzialmente un set di macro glorificato, con il compilatore che crea una nuova copia del codice del modello per ciascun tipo.La prova di ciò è il fatto che un'istanza di MyClass non condividerà una variabile statica con MyClass.Due istanze di MyClass, tuttavia, condivideranno una variabile statica.

/*** 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

In Java, le variabili statiche sono condivise tra istanze di MyClass, indipendentemente dai diversi parametri di tipo.

I generici Java e i modelli C++ presentano una serie di altre differenze.Questi includono:

  • I modelli C++ possono utilizzare tipi primitivi, come int.Java non può e deve invece usare intero.
  • In Java, puoi limitare i parametri del tipo del modello di essere di un certo tipo.Ad esempio, è possibile utilizzare i generici per implementare un carddeck e specificare che il parametro di tipo deve estendersi dal gioco del card.
  • In C ++, il parametro di tipo può essere istanziato, mentre Java non lo supporta.
  • In Java, il parametro di tipo (cioè il foo in myclass) non può essere usato per metodi e variabili statici, poiché questi sarebbero condivisi tra Myclass e Myclass.In C++, queste classi sono diverse, quindi il parametro type può essere utilizzato per metodi e variabili statici.
  • In Java, tutte le istanze di MyClass, indipendentemente dai parametri di tipo, sono dello stesso tipo.I parametri del tipo vengono cancellati in fase di esecuzione.In C++, le istanze con parametri di tipo diversi sono tipi diversi.

I template non sono altro che un macrosistema.Zucchero di sintassi.Vengono completamente espansi prima della compilazione vera e propria (o, almeno, i compilatori si comportano come se fosse così).

Esempio:

Diciamo che vogliamo due funzioni.Una funzione prende due sequenze (lista, array, vettori, qualunque cosa accada) di numeri e restituisce il loro prodotto interno.Un'altra funzione prende una lunghezza, genera due sequenze di quella lunghezza, le passa alla prima funzione e restituisce il risultato.Il problema è che potremmo commettere un errore nella seconda funzione, per cui queste due funzioni non hanno realmente la stessa lunghezza.Abbiamo bisogno che il compilatore ci avvisi in questo caso.Non quando il programma è in esecuzione, ma durante la compilazione.

In Java puoi fare qualcosa del genere:

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");
        }
    }
}

In C# puoi scrivere quasi la stessa cosa.Prova a riscriverlo in C++ e non verrà compilato, lamentando un'espansione infinita di modelli.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top