Pregunta

Esta no es una pregunta de cuál es el boxing y unboxing, es más bien por qué ¿en lenguajes como Java y C# necesito ?

Estoy muy familiarizado con C++, STL y Boost.

En C++ podría escribir algo como esto muy fácilmente,

std::vector<double> dummy;

Tengo algo de experiencia con Java, pero yo estaba realmente sorprendido, porque yo tenía que escribir algo como esto,

ArrayList<Double> dummy = new ArrayList<Double>();

Mi pregunta, ¿por qué debería ser un Objeto, lo que es tan difícil técnicamente para incluir los tipos primitivos cuando se habla acerca de los medicamentos Genéricos ?

¿Fue útil?

Solución

¿por qué es tan difícil técnicamente para incluir los tipos primitivos cuando se habla acerca de los medicamentos Genéricos ?

En Java caso, es debido a la forma en que los medicamentos genéricos funcionan.En Java, los medicamentos genéricos son en tiempo de compilación truco, que le impide poner un Image objeto en un ArrayList<String>.Sin embargo, Java generics se implementan con el tipo de borrado:el tipo genérico de la información se pierde durante el tiempo de ejecución.Esto fue por razones de compatibilidad, debido a que los medicamentos genéricos se han añadido bastante tarde en Java de la vida.Esto significa que, en tiempo de ejecución, una ArrayList<String> efectivamente es una ArrayList<Object> (o mejor:sólo ArrayList que espera y vuelve Object en todos sus métodos) que automáticamente se echa a la String cuando se recupera un valor.

Pero ya int no se derivan de Object, no se puede poner en un ArrayList que espera (en tiempo de ejecución) Object y no puedes lanzar una Object a int cualquiera de los dos.Esto significa que la primitiva int debe ser envuelto en un tipo que hace heredar de Object, como Integer.

C# por ejemplo, funciona de manera diferente.Los medicamentos genéricos en C# también se aplican en tiempo de ejecución y no de boxeo es necesario con un List<int>.Boxeo en C# sólo sucede cuando se intenta almacenar un tipo de valor como int en una variable de tipo de referencia como object.Desde int en C# se hereda de Object en C#, la escritura object obj = 2 es perfectamente válido, sin embargo, el int serán empacados, la cual es realizada automáticamente por el compilador (no Integer tipo de referencia que se expone en el usuario o cualquier cosa).

Otros consejos

El boxeo y el unboxing son una necesidad nacida de la forma en que los lenguajes (como C # y Java) implementan sus estrategias de asignación de memoria.

Ciertos tipos se asignan en la pila y otros en el montón. Para tratar un tipo asignado por pila como un tipo asignado por montón, se requiere el boxeo para mover el tipo asignado por pila al montón. Unboxing es el proceso inverso.

En C #, los tipos asignados a la pila se denominan tipos de valor (por ejemplo, System.Int32 y System.DateTime ) y los tipos asignados al montón son llamados tipos de referencia (por ejemplo, System.Stream y System.String ).

En algunos casos es ventajoso poder tratar un tipo de valor como un tipo de referencia (la reflexión es un ejemplo) pero en la mayoría de los casos, es mejor evitar el encajonamiento y el desempaquetado.

Creo que esto también se debe a que las primitivas no heredan de Object. Supongamos que tiene un método que quiere poder aceptar cualquier cosa como parámetro, por ejemplo.

class Printer {
    public void print(Object o) {
        ...
    }
}

Puede que necesite pasar un valor primitivo simple a ese método, como:

printer.print(5);

Podrías hacerlo sin boxing / unboxing, porque 5 es un primitivo y no un objeto. Podría sobrecargar el método de impresión para cada tipo primitivo para habilitar dicha funcionalidad, pero es una molestia.

Solo puedo decirte para Java por qué no admite tipos primitivos en genéricos.

Primero estaba el problema de que la pregunta para apoyar esto siempre traía la discusión sobre si Java debería tener tipos primitivos. Lo que, por supuesto, dificultó la discusión de la pregunta real.

En segundo lugar, la razón principal para no incluirlo era que querían compatibilidad con versiones anteriores binarias para que se ejecute sin modificaciones en una máquina virtual que no conoce los genéricos. Esta razón de compatibilidad con versiones anteriores / compatibilidad de migración también es la razón por la cual ahora la API de colecciones admite genéricos y se mantuvo igual y no hay (como en C # cuando introdujeron los genéricos) un nuevo conjunto completo de una API de colección compatible con genéricos.

La compatibilidad se realizó utilizando ersure (información de parámetros de tipo genérico eliminada en el momento de la compilación), que también es la razón por la que recibe tantas advertencias de lanzamiento sin verificar en Java.

Todavía podría agregar genéricos reificados, pero no es tan fácil. Solo agregar la información de tipo agregar tiempo de ejecución en lugar de eliminarlo no funcionará ya que rompe la fuente & amp; compatibilidad binaria (no puede continuar usando tipos sin procesar y no puede llamar al código compilado existente porque no tienen los métodos correspondientes).

El otro enfoque es el que eligió C #: ver arriba

Y el autoboxing / unboxing automático no era compatible con este caso de uso porque el autoboxing cuesta demasiado.

Teoría y práctica de Java: problemas genéricos

En Java y C # (a diferencia de C ++) todo extiende Object, por lo que las clases de colección como ArrayList pueden contener Object o cualquiera de sus descendientes (básicamente cualquier cosa).

Por razones de rendimiento, sin embargo, las primitivas en Java, o los tipos de valor en C #, recibieron un estado especial. No son objeto. No puede hacer algo como (en Java):

 7.toString()

Aunque toString es un método en Object. Para unir este guiño al rendimiento, se crearon objetos equivalentes. AutoBoxing elimina el código repetitivo de tener que poner un primitivo en su clase de envoltura y sacarlo nuevamente, haciendo que el código sea más legible.

La diferencia entre los tipos de valor y los objetos en C # es más gris. Vea aquí sobre cómo son diferentes.

Cada objeto sin cadena sin matriz almacenado en el montón contiene un encabezado de 8 o 16 bytes (tamaños para sistemas de 32/64 bits), seguido del contenido de los campos públicos y privados de ese objeto. Las matrices y las cadenas tienen el encabezado anterior, más algunos bytes más que definen la longitud de la matriz y el tamaño de cada elemento (y posiblemente el número de dimensiones, la longitud de cada dimensión adicional, etc.), seguidos de todos los campos del primer elemento, luego todos los campos del segundo, etc. Dada una referencia a un objeto, el sistema puede examinar fácilmente el encabezado y determinar de qué tipo es.

Las ubicaciones de almacenamiento de tipo de referencia contienen un valor de cuatro u ocho bytes que identifica de forma exclusiva un objeto almacenado en el montón. En las implementaciones actuales, ese valor es un puntero, pero es más fácil (y semánticamente equivalente) pensarlo como un '' ID de objeto ''.

Las ubicaciones de almacenamiento de tipo de valor contienen el contenido de los campos del tipo de valor, pero no tienen ningún encabezado asociado. Si el código declara una variable de tipo Int32 , no hay necesidad de almacenar información con ese Int32 diciendo qué es. El hecho de que esa ubicación contenga un Int32 se almacena efectivamente como parte del programa, por lo que no tiene que almacenarse en la ubicación misma. Esto representa un gran ahorro si, por ejemplo, uno tiene un millón de objetos, cada uno de los cuales tiene un campo de tipo Int32 . Cada uno de los objetos que contienen el Int32 tiene un encabezado que identifica la clase que puede operarlo. Dado que una copia de ese código de clase puede operar en cualquiera de los millones de instancias, tener el hecho de que el campo es un Int32 ser parte del código es mucho más eficiente que tener el almacenamiento para cada uno de esos los campos incluyen información sobre lo que es.

El boxeo es necesario cuando se realiza una solicitud para pasar el contenido de una ubicación de almacenamiento de tipo de valor a un código que no sabe esperar ese tipo de valor en particular. El código que espera objetos de tipo desconocido puede aceptar una referencia a un objeto almacenado en el montón. Como cada objeto almacenado en el montón tiene un encabezado que identifica qué tipo de objeto es, el código puede usar ese encabezado siempre que sea necesario usar un objeto de una manera que requiera conocer su tipo.

Tenga en cuenta que en .net, es posible declarar lo que se llaman clases y métodos genéricos. Cada declaración genera automáticamente una familia de clases o métodos que son idénticos, excepto para el tipo de objeto sobre el que esperan actuar. Si uno pasa un Int32 a una rutina DoSomething < T > (T param) , eso generará automáticamente una versión de la rutina en la que cada instancia de tipo T se reemplaza efectivamente con Int32 . Esa versión de la rutina sabrá que cada ubicación de almacenamiento declarada como tipo T contiene un Int32 , así como en el caso donde una rutina estaba codificada para usar un < code> Int32 ubicación de almacenamiento, no será necesario almacenar información de tipo con esas ubicaciones mismas.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top