¿Cuáles son las diferencias entre los tipos “generics” en C ++ y Java?

Java tiene generics y C ++ proporciona un modelo de progtwigción muy fuerte con template s. Entonces, ¿cuál es la diferencia entre los generics de C ++ y Java?

Hay una gran diferencia entre ellos. En C ++ no tiene que especificar una clase o una interfaz para el tipo genérico. Es por eso que puede crear funciones y clases verdaderamente genéricas, con la salvedad de una escritura más flexible.

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

El método anterior agrega dos objetos del mismo tipo, y se puede usar para cualquier tipo T que tenga el operador “+” disponible.

En Java, debe especificar un tipo si desea llamar a métodos sobre los objetos pasados, algo así como:

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

En C ++, las clases / funciones genéricas solo se pueden definir en encabezados, ya que el comstackdor genera diferentes funciones para diferentes tipos (con las que se invoca). Entonces la comstackción es más lenta. En Java, la comstackción no tiene una penalización importante, pero Java usa una técnica llamada “borrado” donde el tipo genérico se borra en el tiempo de ejecución, por lo que en el tiempo de ejecución, Java está llamando …

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

Entonces, la progtwigción genérica en Java no es realmente útil, es solo un poco de azúcar sintáctico para ayudar con la nueva construcción foreach.

EDITAR: la opinión anterior sobre la utilidad fue escrita por un yo más joven. Los generics de Java ayudan con la seguridad de tipo, por supuesto.

Los generics de Java son enormemente diferentes a las plantillas de C ++.

Básicamente, las plantillas de C ++ son básicamente un preprocesador / conjunto de macros glorificado ( Nota: dado que algunas personas parecen incapaces de comprender una analogía, no estoy diciendo que el procesamiento de plantillas sea una macro). En Java, son básicamente azúcares sintácticos para minimizar la conversión repetitiva de Objetos. Aquí hay una introducción bastante decente a las plantillas de C ++ frente a los generics de Java .

Para profundizar sobre este punto: cuando utilizas una plantilla de C ++, básicamente estás creando otra copia del código, como si #define una macro #define . Esto le permite hacer cosas como tener parámetros int en las definiciones de plantilla que determinan los tamaños de las matrices y demás.

Java no funciona así. En Java todos los objetos se extienden desde java.lang.Object , por lo que, antes de los generics, escribirías un código como este:

 public class PhoneNumbers { private Map phoneNumbers = new HashMap(); public String getPhoneNumber(String name) { return (String)phoneNumbers.get(name); } ... } 

porque todos los tipos de colección de Java usaban Object como su tipo base para que pudiera poner cualquier cosa en ellos. Java 5 da vueltas y agrega generics para que pueda hacer cosas como:

 public class PhoneNumbers { private Map phoneNumbers = new HashMap(); public String getPhoneNumber(String name) { return phoneNumbers.get(name); } ... } 

Y eso es todo lo que son generics de Java: envolturas para lanzar objetos. Eso es porque los generics de Java no son refinados. Usan borrado de tipo. Esta decisión se tomó porque Java Generics llegó tan tarde en la pieza que no querían romper la compatibilidad con versiones anteriores (se puede utilizar Map siempre que se solicite un Map ). Compare esto con .Net / C # donde no se usa el borrado de tipo, lo que conduce a todo tipo de diferencias (por ejemplo, puede usar tipos primitivos e IEnumerable e IEnumerable no tienen relación entre sí).

Y una clase que usa generics comstackdos con un comstackdor Java 5+ se puede utilizar en JDK 1.4 (suponiendo que no use ninguna otra característica o clase que requiera Java 5+).

Es por eso que los generics de Java se llaman azúcar sintáctico .

Pero esta decisión sobre cómo hacer generics tiene efectos tan profundos que las (excelentes) preguntas frecuentes de Java Generics han surgido para responder a las muchas, muchas preguntas que las personas tienen sobre Java Generics.

Las plantillas de C ++ tienen una serie de características que los generics de Java no tienen:

  • Uso de argumentos de tipo primitivo.

    Por ejemplo:

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

    Java no permite el uso de argumentos de tipo primitivo en generics.

  • Uso de argumentos de tipo por defecto , que es una característica que echo de menos en Java, pero existen razones de compatibilidad hacia atrás para esto;

  • Java permite el límite de argumentos.

Por ejemplo:

 public class ObservableList { ... } 

Realmente se debe enfatizar que las invocaciones de plantillas con diferentes argumentos realmente son de diferentes tipos. Ni siquiera comparten miembros estáticos. En Java, este no es el caso.

Además de las diferencias con los generics, para completar, aquí hay una comparación básica de C ++ y Java (y otra ).

Y también puedo sugerir Pensar en Java . Como progtwigdor de C ++, muchos de los conceptos como objetos ya serán de naturaleza natural, pero existen diferencias sutiles, por lo que puede valer la pena tener un texto introductorio incluso si se analizan partes.

Gran parte de lo que aprenderá cuando aprenda Java son todas las bibliotecas (ambas estándar, lo que viene en el JDK) y no estándar, que incluye elementos de uso común como Spring). La syntax de Java es más detallada que la syntax de C ++ y no tiene muchas características C ++ (por ejemplo, sobrecarga del operador, herencia múltiple, mecanismo destructor, etc.), pero eso tampoco lo convierte estrictamente en un subconjunto de C ++.

C ++ tiene plantillas. Java tiene generics, que se parecen mucho a las plantillas de C ++, pero son muy, muy diferentes.

Las plantillas funcionan, como su nombre lo indica, al proporcionar al comstackdor una plantilla (espere …) que puede usar para generar código de tipo seguro rellenando los parámetros de la plantilla.

Los generics, tal como los entiendo, funcionan al revés: los parámetros de tipo son utilizados por el comstackdor para verificar que el código que los utiliza es seguro para el tipo, pero el código resultante se genera sin ningún tipo de tipo.

Piense en las plantillas de C ++ como un macro sistema realmente bueno , y en los generics de Java como una herramienta para generar automáticamente tipos de letra.

Otra característica que tienen las plantillas de C ++ que los generics de Java no tienen es la especialización. Eso le permite tener una implementación diferente para tipos específicos. Por lo tanto, puede, por ejemplo, tener una versión altamente optimizada para una int , mientras que todavía tiene una versión genérica para el rest de los tipos. O puede tener diferentes versiones para tipos de puntero y no puntero. Esto es útil si desea operar en el objeto desreferenciado cuando se le entrega un puntero.

Hay una gran explicación de este tema en Java Generics and Collections Por Maurice Naftalin, Philip Wadler. Recomiendo altamente este libro. Citar:

Los generics en Java se parecen a las plantillas en C ++. … La syntax es deliberadamente similar y la semántica es deliberadamente diferente. … Semánticamente, los generics de Java se definen por borrado, mientras que las plantillas de C ++ se definen por expansión.

Por favor, lea la explicación completa aquí .

texto alternativo http://sofes.miximages.com/java/0596527756_cat.gif

Básicamente, las plantillas AFAIK y C ++ crean una copia del código para cada tipo, mientras que los generics de Java usan exactamente el mismo código.

Sí, puedes decir que la plantilla de C ++ es equivalente al concepto genérico de Java (aunque más correctamente sería decir que los generics de Java son equivalentes a C ++ en concepto)

Si está familiarizado con el mecanismo de plantilla de C ++, podría pensar que los generics son similares, pero la similitud es superficial. Los generics no generan una nueva clase para cada especialización ni permiten la “metaprogtwigción de plantillas”.

de: Java Generics

Otra ventaja de las plantillas de C ++ es la especificación.

  T sum(T a, T b) { return a + b; }  T sum(T* a, T* b) { return (*a) + (*b); } Special sum(const Special& a, const Special& b) { return a.plus(b); } 

Ahora, si llama a sum con punteros, se llamará al segundo método, si llama a sum con objetos no punteros, se llamará al primer método, y si llama a sum () con objetos especiales, se llamará al tercero. No creo que esto sea posible con Java.

Los generics Java (y C #) parecen ser un mecanismo simple de sustitución del tipo de tiempo de ejecución.
Las plantillas de C ++ son una construcción en tiempo de comstackción que le proporciona una forma de modificar el idioma para adaptarlo a sus necesidades. En realidad, son un lenguaje puramente funcional que el comstackdor ejecuta durante una comstackción.

Lo resumiré en una sola oración: las plantillas crean nuevos tipos, los generics restringen los tipos existentes.

@Keith:

Ese código es realmente incorrecto y, aparte de las fallas técnicas más pequeñas (la template omite, la syntax de especialización se ve de manera diferente), la especialización parcial no funciona en las plantillas de funciones, solo en las plantillas de clase. Sin embargo, el código funcionaría sin una especialización parcial de la plantilla, en lugar de usar una vieja y simple sobrecarga:

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

Las plantillas no son más que un macro sistema. Sintaxis de azúcar. Están completamente expandidos antes de la comstackción real (o, al menos, los comstackdores se comportan como si fuera el caso).

Ejemplo:

Digamos que queremos dos funciones. Una función toma dos secuencias (lista, matrices, vectores, lo que sea que pase) de números y devuelve su producto interno. Otra función toma una longitud, genera dos secuencias de esa longitud, las pasa a la primera función y devuelve su resultado. La trampa es que podríamos cometer un error en la segunda función, de modo que estas dos funciones no son realmente de la misma longitud. Necesitamos que el comstackdor nos advierta en este caso. No cuando el progtwig se está ejecutando, sino cuando se está comstackndo.

En Java puedes hacer algo como esto:

 import java.io.*; interface ScalarProduct { public Integer scalarProduct(A second); } class Nil implements ScalarProduct{ Nil(){} public Integer scalarProduct(Nil second) { return 0; } } class Cons> implements ScalarProduct>{ public Integer value; public A tail; Cons(Integer _value, A _tail) { value = _value; tail = _tail; } public Integer scalarProduct(Cons 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 > 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(2*i+1,first), new Cons(i*i, second)); //the following line won't compile, it produces an error: //return _main(n-1, i+1, first, new Cons(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"); } } } 

En C # puedes escribir casi lo mismo. Intenta volver a escribirlo en C ++, y no se comstackrá, quejándose de la expansión infinita de plantillas.

La respuesta a continuación es del libro Cracking The Coding Interview Solutions para el Capítulo 13, que creo que es muy bueno.

La implementación de los generics de Java se basa en una idea de “borrado de tipos:” Esta técnica elimina los tipos parametrizados cuando el código fuente se traduce al bytecode de la Máquina Virtual Java (JVM). Por ejemplo, supongamos que tiene el siguiente código Java:

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

Durante la comstackción, este código se vuelve a escribir en:

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

El uso de los generics de Java realmente no cambió mucho nuestras capacidades; simplemente hizo las cosas un poco más bonitas. Por esta razón, los generics de Java a veces se llaman “azúcar sintáctico:”.

Esto es bastante diferente de C ++. En C ++, las plantillas son esencialmente un macro conjunto glorificado, con el comstackdor creando una nueva copia del código de plantilla para cada tipo. Prueba de esto es el hecho de que una instancia de MyClass no compartirá una variable estática con MyClass. Las instancias de remolque de MyClass, sin embargo, compartirán una variable estática.

 /*** MyClass.h ***/ template class MyClass { public: static int val; MyClass(int v) { val v;} }; /*** MyClass.cpp ***/ template int MyClass::bar; template class MyClass; template class MyClass; /*** main.cpp ***/ MyClass * fool MyClass * foo2 MyClass * barl MyClass * bar2 new MyClass(10); new MyClass(15); new MyClass(20); new MyClass(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 

En Java, las variables estáticas se comparten entre instancias de MyClass, independientemente de los parámetros de tipo diferente.

Los generics de Java y las plantillas C ++ tienen otras diferencias. Éstas incluyen:

  • Las plantillas C ++ pueden usar tipos primitivos, como int. Java no puede y debe usar Entero.
  • En Java, puede restringir los parámetros de tipo de la plantilla para que sean de cierto tipo. Por ejemplo, puede usar generics para implementar CardDeck y especificar que el parámetro de tipo debe extenderse desde CardGame.
  • En C ++, el parámetro tipo se puede instanciar, mientras que Java no lo admite.
  • En Java, el parámetro tipo (es decir, el Foo en MyClass) no se puede utilizar para métodos y variables estáticos, ya que estos se compartirían entre MyClass y MyClass. En C ++, estas clases son diferentes, por lo que el parámetro tipo se puede usar para métodos estáticos y variables.
  • En Java, todas las instancias de MyClass, independientemente de sus parámetros de tipo, son del mismo tipo. Los parámetros de tipo se borran en el tiempo de ejecución. En C ++, las instancias con diferentes parámetros de tipo son diferentes tipos.