studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones java c++ generics templates language-features

programacion - ¿Cuáles son las diferencias entre los tipos "genéricos" en C++ y Java?



manual de programacion android pdf (11)

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


@Keith:

Ese código es realmente incorrecto y, aparte de las fallas técnicas más pequeñas (la template omite, la sintaxis 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 <typename T> T sum(T a, T b) { return a + b; } template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }


Básicamente, las plantillas AFAIK y C ++ crean una copia del código para cada tipo, mientras que los genéricos 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 genéricos de Java son equivalentes a C ++ en concepto)

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

de: Java Generics


C ++ tiene plantillas. Java tiene genéricos, 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 compilador una plantilla (espere ...) que puede usar para generar código de tipo seguro rellenando los parámetros de la plantilla.

Los genéricos, tal como los entiendo, funcionan al revés: los parámetros de tipo son utilizados por el compilador 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 genéricos de Java como una herramienta para generar automáticamente tipos de letra.


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 <typename T> 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 extends Something> 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 compilador genera diferentes funciones para diferentes tipos (con las que se invoca). Entonces la compilación es más lenta. En Java, la compilació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 programació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 genéricos de Java ayudan con la seguridad de tipo, por supuesto.


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

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

Por favor, lea la explicación completa here .

texto alternativo http://oreilly.com/catalog/covers/0596527756_cat.gif


Las plantillas no son más que un macro sistema. Sintaxis de azúcar. Están completamente expandidos antes de la compilación real (o, al menos, los compiladores 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 compilador nos advierta en este caso. No cuando el programa se está ejecutando, sino cuando se está compilando.

En Java puedes hacer algo como esto:

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

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


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


Los genéricos 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 compilación que le proporciona una forma de modificar el idioma para adaptarlo a sus necesidades. En realidad, son un lenguaje puramente funcional que el compilador ejecuta durante una compilación.


Los genéricos 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 genéricos 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 java.lang.Object desde java.lang.Object , por lo que, antes de los genéricos, 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 genéricos para que pueda hacer cosas como:

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

Y eso es todo lo que son genéricos de Java: envolturas para lanzar objetos. Eso es porque los genéricos 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<String, String> 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<T> no tienen relación entre sí).

Y una clase que usa genéricos compilados con un compilador 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 genéricos de Java se llaman azúcar sintáctico .

Pero esta decisión sobre cómo hacer genéricos 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 genéricos de Java no tienen:

  • Uso de argumentos de tipo primitivo.

    Por ejemplo:

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

    Java no permite el uso de argumentos de tipo primitivo en genéricos.

  • 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<T extends List> { ... }

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 genéricos, para completar, aquí hay una comparación básica de C ++ y Java (y otra ).

Y también puedo sugerir Pensar en Java . Como programador 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 sintaxis de Java es más detallada que la sintaxis 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 ++.


Otra característica que tienen las plantillas de C ++ que los genéricos 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 resto 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.


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

<typename T> T sum(T a, T b) { return a + b; } <typename T> 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.