Tenga en cuenta que la razón por la que existen tipos sin formato es la compatibilidad con versiones anteriores de Java 1.4 y anteriores, que no tenían genéricos en absoluto.
Jesper
Respuestas:
745
¿Qué es un tipo crudo?
La especificación del lenguaje Java define un tipo sin procesar de la siguiente manera:
El tipo de referencia que se forma tomando el nombre de una declaración de tipo genérico sin una lista de argumentos de tipo acompañante.
Un tipo de matriz cuyo tipo de elemento es un tipo sin formato.
Un statictipo no miembro de un tipo sin formato Rque no se hereda de una superclase o superinterfaz de R.
Aquí hay un ejemplo para ilustrar:
publicclassMyType<E>{classInner{}staticclassNested{}publicstaticvoid main(String[] args){MyType mt;// warning: MyType is a raw typeMyType.Inner inn;// warning: MyType.Inner is a raw typeMyType.Nested nest;// no warning: not parameterized typeMyType<Object> mt1;// no warning: type parameter givenMyType<?> mt2;// no warning: type parameter given (wildcard OK!)}}
Aquí, MyType<E>es un tipo parametrizado ( JLS 4.5 ). Es común referirse coloquialmente a este tipo simplemente MyTypepara abreviar, pero técnicamente el nombre es MyType<E>.
mttiene un tipo sin formato (y genera una advertencia de compilación) por el primer punto en la definición anterior; inntambién tiene un tipo sin formato en el tercer punto de viñeta
MyType.Nestedno es un tipo parametrizado, aunque es un tipo de miembro de un tipo parametrizado MyType<E>, porque lo es static.
mt1, y mt2ambos se declaran con parámetros de tipo reales, por lo que no son tipos sin formato.
¿Qué tienen de especial los tipos sin procesar?
Esencialmente, los tipos sin procesar se comportan tal como eran antes de que se introdujeran los genéricos. Es decir, lo siguiente es completamente legal en tiempo de compilación.
List names =newArrayList();// warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE);// not a compilation error!
El código anterior funciona bien, pero supongamos que también tiene lo siguiente:
for(Object o : names){String name =(String) o;System.out.println(name);}// throws ClassCastException!// java.lang.Boolean cannot be cast to java.lang.String
Ahora nos encontramos con problemas en tiempo de ejecución, porque namescontiene algo que no es un instanceof String.
Presumiblemente, si solo desea namescontener String, tal vez aún podría usar un tipo sin formato y verificar manualmente cadaadd uno, y luego emitir manualmente a Stringcada elemento desde names. Aún mejor , aunque NO es usar un tipo sin procesar y dejar que el compilador haga todo el trabajo por usted , aprovechando el poder de los genéricos de Java.
¿En qué se diferencia un tipo sin formato de usar <Object>como parámetros de tipo?
La siguiente es una cita de Effective Java 2nd Edition, Artículo 23: No use tipos sin procesar en el nuevo código :
¿Cuál es la diferencia entre el tipo sin formato Listy el tipo parametrizado List<Object>? Hablando en términos generales, el primero ha optado por la verificación de tipos genéricos, mientras que el segundo le dijo explícitamente al compilador que es capaz de contener objetos de cualquier tipo. Si bien puede pasar List<String>a un parámetro de tipo List, no puede pasarlo a un parámetro de tipo List<Object>. Hay reglas de subtipo para genéricos, y List<String>es un subtipo del tipo sin formato List, pero no del tipo parametrizado List<Object>. Como consecuencia, pierde seguridad de tipo si usa un tipo sin formato como List, pero no si usa un tipo con parámetros comoList<Object> .
Para ilustrar el punto, considere el siguiente método que toma List<Object>ay agrega a new Object().
Si hubiera declarado appendNewObjecttomar un tipo sin formato Listcomo parámetro, esto se compilaría y, por lo tanto, perdería la seguridad de tipo que obtiene de los genéricos.
¿En qué se diferencia un tipo sin formato del uso <?>como parámetro de tipo?
List<Object>, List<String>etc. son todos List<?>, por lo que puede ser tentador decir que son simplemente en su Listlugar. Sin embargo, hay una gran diferencia: dado que a List<E>solo define add(E), no puede agregar cualquier objeto arbitrario a a List<?>. Por otro lado, dado que el tipo sin Listformato no tiene seguridad de tipo, puede addcasi cualquier cosa para a List.
Considere la siguiente variación del fragmento anterior:
staticvoid appendNewObject(List<?> list){
list.add(newObject());// compilation error!}//...List<String> names =newArrayList<String>();
appendNewObject(names);// this part is fine!
¡El compilador hizo un trabajo maravilloso al protegerlo de violar potencialmente la invariancia de tipo del List<?>! Si hubiera declarado el parámetro como tipo sin formato List list, el código se compilaría y violaría el tipo invariante de List<String> names.
Un tipo sin formato es el borrado de ese tipo
Volver a JLS 4.8:
Es posible utilizar como tipo la eliminación de un tipo parametrizado o la eliminación de un tipo de matriz cuyo tipo de elemento es un tipo parametrizado. Tal tipo se llama tipo crudo .
[...]
Las superclases (respectivamente, superinterfaces) de un tipo sin formato son los borrados de las superclases (superinterfaces) de cualquiera de las parametrizaciones del tipo genérico.
El tipo de constructor, método de instancia o no staticcampo de un tipo sin formato Cque no se hereda de sus superclases o superinterfaces es el tipo sin formato que corresponde a la eliminación de su tipo en la declaración genérica correspondiente a C.
En términos más simples, cuando se usa un tipo sin formato, los constructores, los métodos de instancia y los no staticcampos también se borran .
El borrado de tipos también asigna la firma de un constructor o método a una firma que no tiene tipos parametrizados o variables de tipo. El borrado de una firma de constructor o método ses una firma que consta del mismo nombre sy borra todos los tipos de parámetros formales dados en s.
El tipo de retorno de un método y los parámetros de tipo de un método genérico o constructor también se borran si se borra la firma del método o del constructor.
La eliminación de la firma de un método genérico no tiene parámetros de tipo.
El siguiente informe de error contiene algunas reflexiones de Maurizio Cimadamore, un desarrollador del compilador, y Alex Buckley, uno de los autores de JLS, sobre por qué debería ocurrir este tipo de comportamiento: https://bugs.openjdk.java.net/browse / JDK-6400189 . (En resumen, simplifica la especificación).
Si no es seguro, ¿por qué está permitido usar un tipo sin formato?
Aquí hay otra cita de JLS 4.8:
El uso de tipos sin formato solo se permite como una concesión a la compatibilidad del código heredado. Se desaconseja el uso de tipos sin formato en el código escrito después de la introducción de la genérica en el lenguaje de programación Java. Es posible que futuras versiones del lenguaje de programación Java no permitan el uso de tipos sin formato.
Efectivo Java 2nd Edition también tiene esto para agregar:
Dado que no debe usar tipos sin formato, ¿por qué los diseñadores de idiomas lo permitieron? Para proporcionar compatibilidad.
La plataforma Java estaba a punto de entrar en su segunda década cuando se introdujeron los genéricos, y existía una enorme cantidad de código Java que no usaba genéricos. Se consideró crítico que todo este código siga siendo legal e interoperable con el nuevo código que usa genéricos. Tenía que ser legal pasar instancias de tipos parametrizados a métodos diseñados para su uso con tipos normales, y viceversa. Este requisito, conocido como compatibilidad de migración , llevó a la decisión de admitir tipos sin formato.
En resumen, los tipos sin formato NUNCA deben usarse en un código nuevo. Siempre debe usar tipos parametrizados .
¿No hay excepciones?
Desafortunadamente, debido a que los genéricos de Java no están reificados, hay dos excepciones en las que se deben usar tipos sin formato en el nuevo código:
Literales de clase, por ejemplo List.class, noList<String>.class
instanceofoperando, por ejemplo o instanceof Set, noo instanceof Set<String>
¿Qué quiere decir que "los genéricos de Java no están reificados"?
Carl G
77
Para la segunda excepción, la sintaxis o instanceof Set<?>también permite evitar el tipo sin formato (aunque en este caso solo es superficial).
Paul Bellora
1
Los tipos sin formato son muy útiles y reducen el código repetitivo en caso de búsquedas JNDI para un bean que extiende una interfaz. Esto resuelve la necesidad de escribir nbeans remotos para cada clase de implementación con código idéntico.
djmj
8
"No reificado" es otra forma de decir que se borran. El compilador sabe cuáles son los parámetros genéricos, pero esa información no se transmite al código de bytes generado. El JLS requiere que los literales de clase no tengan parámetros de tipo.
Erick G. Hagstrom
2
@OldCurmudgeon Eso es interesante. Quiero decir oficialmente que no es ninguno , porque un literal de clase definido como justo TypeName.class, donde TypeNamees un identificador simple ( jls ). Hablando hipotéticamente, supongo que realmente podría ser cualquiera. Tal vez como una pista, List<String>.classes la variante que JLS llama específicamente un error del compilador, por lo que si alguna vez lo agregan al lenguaje, esperaría que ese sea el que usan.
Radiodef
62
¿Qué son los tipos sin formato en Java y por qué a menudo escucho que no deberían usarse en un código nuevo?
Los tipos sin procesar son historia antigua del lenguaje Java. Al principio hubo Collectionsy no tenían Objectsnada más y nada menos. Cada operación en Collectionslos moldes requeridos del Objecttipo deseado.
List aList =newArrayList();String s ="Hello World!";
aList.add(s);String c =(String)aList.get(0);
Si bien esto funcionó la mayor parte del tiempo, ocurrieron errores
List aNumberList =newArrayList();String one ="1";//Number one
aNumberList.add(one);Integer iOne =(Integer)aNumberList.get(0);//Insert ClassCastException here
Las antiguas colecciones sin tipo no podían hacer cumplir la seguridad de tipo, por lo que el programador tuvo que recordar lo que almacenaba dentro de una colección.
Los genéricos se inventaron para evitar esta limitación, el desarrollador declararía el tipo almacenado una vez y el compilador lo haría en su lugar.
List<String> aNumberList =newArrayList<String>();
aNumberList.add("one");Integer iOne = aNumberList.get(0);//Compile time errorString sOne = aNumberList.get(0);//works fine
Para comparacion:
// Old style collections now known as raw typesList aList =newArrayList();//Could contain anything// New style collections with GenericsList<String> aList =newArrayList<String>();//Contains only Strings
Más compleja es la interfaz Comparable:
//raw, not type save can compare with Other classesclassMyCompareAbleimplementsCompareAble{int id;publicint compareTo(Object other){returnthis.id -((MyCompareAble)other).id;}}//GenericclassMyCompareAbleimplementsCompareAble<MyCompareAble>{int id;publicint compareTo(MyCompareAble other){returnthis.id - other.id;}}
Tenga en cuenta que es imposible implementar la CompareAbleinterfaz con compareTo(MyCompareAble)tipos sin formato. Por qué no deberías usarlos:
Todo lo Objectalmacenado en un Collectiondebe ser lanzado antes de poder ser utilizado.
El uso de genéricos permite comprobaciones de tiempo de compilación
Usar tipos sin formato es lo mismo que almacenar cada valor como Object
Lo que hace el compilador: los genéricos son compatibles con versiones anteriores, usan las mismas clases de Java que los tipos sin formato. La magia ocurre principalmente en tiempo de compilación.
List<String> someStrings =newArrayList<String>();
someStrings.add("one");String one = someStrings.get(0);
Será compilado como:
List someStrings =newArrayList();
someStrings.add("one");String one =(String)someStrings.get(0);
Este es el mismo código que escribiría si utilizara los tipos sin formato directamente. Aunque no estoy seguro de lo que sucede con la CompareAbleinterfaz, creo que crea dos compareTofunciones, una toma una MyCompareAbley la otra toma una Objecty se la pasa a la primera después de lanzarla.
¿Cuáles son las alternativas a los tipos sin procesar: usar genéricos
Para crear un tipo parametrizado de Box<T>, debe proporcionar un argumento de tipo real para el parámetro de tipo formal T:
Box<Integer> intBox =newBox<>();
Si se omite el argumento de tipo real, crea un tipo sin formato de Box<T>:
Box rawBox =newBox();
Por lo tanto, Boxes el tipo sin formato del tipo genérico Box<T>. Sin embargo, una clase no genérica o tipo de interfaz no es un tipo sin formato.
Los tipos sin formato aparecen en el código heredado porque muchas clases de API (como las clases de Colecciones) no eran genéricas antes de JDK 5.0. Cuando se usan tipos sin procesar, esencialmente se obtiene un comportamiento pre-genérico: a Boxda Objects. Por compatibilidad con versiones anteriores, se permite asignar un tipo parametrizado a su tipo sin formato:
Box<String> stringBox =newBox<>();Box rawBox = stringBox;// OK
Pero si asigna un tipo sin formato a un tipo parametrizado, recibirá una advertencia:
Box rawBox =newBox();// rawBox is a raw type of Box<T>Box<Integer> intBox = rawBox;// warning: unchecked conversion
También recibe una advertencia si usa un tipo sin procesar para invocar métodos genéricos definidos en el tipo genérico correspondiente:
La advertencia muestra que los tipos sin procesar omiten las comprobaciones de tipos genéricos, aplazando la captura de código inseguro en tiempo de ejecución. Por lo tanto, debe evitar el uso de tipos sin formato.
La sección de borrado de tipo tiene más información sobre cómo el compilador de Java usa tipos sin formato.
Mensajes de error no verificados
Como se mencionó anteriormente, al mezclar código heredado con código genérico, puede encontrar mensajes de advertencia similares a los siguientes:
Nota: Example.java utiliza operaciones no verificadas o inseguras.
Nota: Recompile con -Xlint: sin marcar para más detalles.
Esto puede suceder cuando se usa una API anterior que funciona con tipos sin formato, como se muestra en el siguiente ejemplo:
publicclassWarningDemo{publicstaticvoid main(String[] args){Box<Integer> bi;
bi = createBox();}staticBox createBox(){returnnewBox();}}
El término "desmarcado" significa que el compilador no tiene suficiente información de tipo para realizar todas las verificaciones de tipo necesarias para garantizar la seguridad de tipo. La advertencia "sin marcar" está deshabilitada, de forma predeterminada, aunque el compilador da una pista. Para ver todas las advertencias "sin marcar", vuelva a compilar con -Xlint: sin marcar.
Volver a compilar el ejemplo anterior con -Xlint: sin marcar revela la siguiente información adicional:
WarningDemo.java:4: warning:[unchecked] unchecked conversion
found :Box
required:Box<java.lang.Integer>
bi = createBox();^1 warning
Para deshabilitar completamente las advertencias no verificadas, use el indicador -Xlint: -unchecked. La @SuppressWarnings("unchecked")anotación suprime las advertencias no verificadas. Si no está familiarizado con la @SuppressWarningssintaxis, vea Anotaciones.
Un tipo "en bruto" en Java es una clase que no es genérica y trata con objetos "en bruto", en lugar de parámetros de tipo genérico con seguridad de tipo.
Por ejemplo, antes de que los genéricos de Java estuvieran disponibles, usaría una clase de colección como esta:
LinkedList list =newLinkedList();
list.add(newMyObject());MyObject myObject =(MyObject)list.get(0);
Cuando agrega su objeto a la lista, no le importa qué tipo de objeto es, y cuando lo obtiene de la lista, debe convertirlo explícitamente al tipo que espera.
Usando genéricos, elimina el factor "desconocido", porque debe especificar explícitamente qué tipo de objetos pueden ir en la lista:
LinkedList<MyObject> list =newLinkedList<MyObject>();
list.add(newMyObject());MyObject myObject = list.get(0);
Tenga en cuenta que con los genéricos no tiene que emitir el objeto proveniente de la llamada get, la colección está predefinida para funcionar solo con MyObject. Este mismo hecho es el principal factor impulsor de los genéricos. Cambia una fuente de errores de tiempo de ejecución en algo que se puede verificar en tiempo de compilación.
Más específicamente, un tipo sin formato es lo que obtienes cuando simplemente omites los parámetros de tipo para un tipo genérico. Los tipos sin formato solo eran en realidad una característica de compatibilidad con versiones anteriores, y están potencialmente sujetos a eliminación. Puede obtener un comportamiento similar usando? Parámetros comodín.
John Flatness
@zerocrates: ¡similar pero diferente! Usar ?todavía ofrece seguridad de tipo. Lo cubrí en mi respuesta.
Polygenelubricants
19
privatestaticList<String> list =newArrayList<String>();
Debe especificar el tipo-parámetro.
La advertencia informa que los tipos definidos para admitir genéricos deben ser parametrizados, en lugar de usar su forma sin formato.
Listse define a los genéricos de apoyo: public class List<E>. Esto permite muchas operaciones de tipo seguro, que se verifican en tiempo de compilación.
Ahora reemplazado por la inferencia de diamantes en Java 7 -private static List<String> list = new ArrayList<>();
Ian Campbell
14
¿Qué es un tipo sin formato y por qué a menudo escucho que no deberían usarse en un código nuevo?
Un "tipo sin formato" es el uso de una clase genérica sin especificar un argumento de tipo (s) para su tipo (s) parametrizado, por ejemplo, usando en Listlugar de List<String>. Cuando se introdujeron los genéricos en Java, se actualizaron varias clases para usar los genéricos. El uso de estas clases como un "tipo sin formato" (sin especificar un argumento de tipo) permitió que el código heredado aún se compilara.
Los "tipos sin formato" se utilizan para la compatibilidad con versiones anteriores. No se recomienda su uso en código nuevo porque el uso de la clase genérica con un argumento de tipo permite una escritura más fuerte, lo que a su vez puede mejorar la comprensión del código y conducir a detectar problemas potenciales antes.
¿Cuál es la alternativa si no podemos usar tipos sin formato y cómo es mejor?
La alternativa preferida es utilizar clases genéricas según lo previsto, con un argumento de tipo adecuado (por ejemplo List<String>). Esto permite que el programador especifique tipos más específicamente, transmite más significado a los futuros mantenedores sobre el uso previsto de una estructura de datos o variable, y permite al compilador hacer cumplir una mejor seguridad de tipos. Estas ventajas juntas pueden mejorar la calidad del código y ayudar a prevenir la introducción de algunos errores de codificación.
Por ejemplo, para un método donde el programador quiere asegurarse de que una variable de Lista llamada 'nombres' contenga solo Cadenas:
List<String> names =newArrayList<String>();
names.add("John");// OK
names.add(newInteger(1));// compile error
Ah, estoy tentado de copiar polygenelubricantslas referencias de "tipo sin formato " de stackoverflow.com/questions/2770111/… en mi propia respuesta, pero supongo que las dejaré para usar en su propia respuesta.
Bert F
1
Sí, esencialmente he estado copiando y pegando ese segmento en todas partes donde las personas usan tipos sin procesar en stackoverflow, y finalmente decidí tener una pregunta para hacer referencia a partir de ahora. Espero que sea una buena contribución para la comunidad.
Polygenelubricants
1
@polygenelubricants que noté - respondimos algunas de las mismas preguntas :-)
Bert F
1
@ ha9u63ar: De hecho. En general, las respuestas concisas y simples son al menos tan buenas como las largas y aceptadas.
displayName
¿Qué es un "syping más fuerte"?
carloswm85
12
El compilador quiere que escribas esto:
privatestaticList<String> list =newArrayList<String>();
porque de lo contrario, podría agregar cualquier tipo que desee list, haciendo que la instanciación no tenga new ArrayList<String>()sentido. Los genéricos de Java son solo una característica de tiempo de compilación, por lo que un objeto creado con new ArrayList<String>()felicidad aceptará elementos Integero JFrameelementos si se les asigna una referencia del "tipo sin formato" List: el objeto en sí mismo no sabe nada sobre los tipos que se supone que debe contener, solo el compilador lo sabe.
ArrayList<String> arres una ArrayListvariable de referencia con tipo Stringque hace referencia a un ArralyListObjeto de tipo String. Significa que solo puede contener objetos de tipo cadena.
Es estricto Stringno un tipo sin procesar, por lo que nunca generará una advertencia.
arr.add("hello");// alone statement will compile successfully and no warning.
arr.add(23);//prone to compile time error.//error: no suitable method found for add(int)
Caso 2
En este caso ArrayList<String> arres un tipo estricto pero su objeto new ArrayList();es un tipo sin formato.
arr.add("hello");//alone this compile but raise the warning.
arr.add(23);//again prone to compile time error.//error: no suitable method found for add(int)
Aquí arrhay un tipo estricto. Por lo tanto, generará un error de tiempo de compilación al agregar a integer.
Advertencia : - Un Rawobjeto de tipo está referenciado a una Strictvariable de tipo referenciada de ArrayList.
Caso 3
En este caso ArrayList arres un tipo sin formato, pero su objeto new ArrayList<String>();es un tipo estricto.
arr.add("hello");
arr.add(23);//compiles fine but raise the warning.
Agregará cualquier tipo de objeto porque arres un tipo sin formato.
Advertencia : - Un Strictobjeto de tipo está referenciado a una rawvariable de tipo referenciado.
Un tipo sin formato es la falta de un parámetro de tipo cuando se usa un tipo genérico.
El tipo sin formato no se debe usar porque podría causar errores de tiempo de ejecución, como insertar una doubleen lo que se suponía que era una Setde ints.
Set set =newHashSet();
set.add(3.45);//ok
Al recuperar las cosas del Set, no sabes lo que está saliendo. Supongamos que espera que sea todo ints, lo está emitiendo Integer; excepción en tiempo de ejecución cuando doublellega el 3.45.
Con un parámetro de tipo agregado a su Set, obtendrá un error de compilación de inmediato. Este error preventivo le permite solucionar el problema antes de que algo explote durante el tiempo de ejecución (ahorrando así tiempo y esfuerzo).
Set<Integer> set =newHashSet<Integer>();
set.add(3.45);//NOT ok.
Como se mencionó en la respuesta aceptada, pierde todo el soporte para genéricos dentro del código de tipo sin formato. Cada parámetro de tipo se convierte en su borrado (que en el ejemplo anterior es justo Object).
Lo que está diciendo es que tu listes un Listobjeto no especificado. Es decir, Java no sabe qué tipo de objetos están dentro de la lista. Luego, cuando desee iterar la lista, debe convertir cada elemento para poder acceder a las propiedades de ese elemento (en este caso, Cadena).
En general, es una mejor idea parametrizar las colecciones, para que no tenga problemas de conversión, solo podrá agregar elementos del tipo parametrizado y su editor le ofrecerá los métodos adecuados para seleccionar.
privatestaticList<String> list =newArrayList<String>();
Los tipos sin formato se refieren al uso de un tipo genérico sin especificar un parámetro de tipo.
Por ejemplo ,
Una lista es un tipo sin formato, mientras que List<String>es un tipo parametrizado.
Cuando se introdujeron los genéricos en JDK 1.5, los tipos sin formato se conservaron solo para mantener la compatibilidad con versiones anteriores de Java. Aunque todavía es posible usar tipos sin formato,
Deben evitarse :
Por lo general requieren moldes
No son de tipo seguro, y algunos tipos importantes de errores solo aparecerán en tiempo de ejecución
Son menos expresivos y no se documentan de la misma manera que los tipos parametrizados
Ejemplo
import java.util.*;publicfinalclassAvoidRawTypes{void withRawType(){//Raw List doesn't self-document, //doesn't state explicitly what it can containList stars =Arrays.asList("Arcturus","Vega","Altair");Iterator iter = stars.iterator();while(iter.hasNext()){String star =(String) iter.next();//cast needed
log(star);}}void withParameterizedType(){List<String> stars =Arrays.asList("Spica","Regulus","Antares");for(String star: stars){
log(star);}}privatevoid log(Object message){System.out.println(Objects.toString(message));}}
Encontré esta página después de hacer algunos ejercicios de muestra y tener exactamente el mismo desconcierto.
============== Pasé de este código como lo proporciona la muestra ===============
publicstaticvoid main(String[] args)throwsIOException{Map wordMap =newHashMap();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator i = wordMap.entrySet().iterator(); i.hasNext();){Map.Entry entry =(Map.Entry) i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}
====================== A este código ========================
publicstaticvoid main(String[] args)throwsIOException{// replace with TreeMap to get them sorted by nameMap<String,Integer> wordMap =newHashMap<String,Integer>();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator<Entry<String,Integer>> i = wordMap.entrySet().iterator(); i.hasNext();){Entry<String,Integer> entry = i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}}
Los tipos sin procesar están bien cuando expresan lo que quieres expresar.
Por ejemplo, una función de deserialización puede devolver a List, pero no conoce el tipo de elemento de la lista. Entonces, Listes el tipo de retorno apropiado aquí.
Respuestas:
¿Qué es un tipo crudo?
La especificación del lenguaje Java define un tipo sin procesar de la siguiente manera:
Tipos sin procesar de JLS 4.8
Aquí hay un ejemplo para ilustrar:
Aquí,
MyType<E>
es un tipo parametrizado ( JLS 4.5 ). Es común referirse coloquialmente a este tipo simplementeMyType
para abreviar, pero técnicamente el nombre esMyType<E>
.mt
tiene un tipo sin formato (y genera una advertencia de compilación) por el primer punto en la definición anterior;inn
también tiene un tipo sin formato en el tercer punto de viñetaMyType.Nested
no es un tipo parametrizado, aunque es un tipo de miembro de un tipo parametrizadoMyType<E>
, porque lo esstatic
.mt1
, ymt2
ambos se declaran con parámetros de tipo reales, por lo que no son tipos sin formato.¿Qué tienen de especial los tipos sin procesar?
Esencialmente, los tipos sin procesar se comportan tal como eran antes de que se introdujeran los genéricos. Es decir, lo siguiente es completamente legal en tiempo de compilación.
El código anterior funciona bien, pero supongamos que también tiene lo siguiente:
Ahora nos encontramos con problemas en tiempo de ejecución, porque
names
contiene algo que no es uninstanceof String
.Presumiblemente, si solo desea
names
contenerString
, tal vez aún podría usar un tipo sin formato y verificar manualmente cadaadd
uno, y luego emitir manualmente aString
cada elemento desdenames
. Aún mejor , aunque NO es usar un tipo sin procesar y dejar que el compilador haga todo el trabajo por usted , aprovechando el poder de los genéricos de Java.Por supuesto, si QUÉ desea
names
para permitir unaBoolean
, entonces se puede declarar comoList<Object> names
, y el código anterior se compilará.Ver también
¿En qué se diferencia un tipo sin formato de usar
<Object>
como parámetros de tipo?La siguiente es una cita de Effective Java 2nd Edition, Artículo 23: No use tipos sin procesar en el nuevo código :
Para ilustrar el punto, considere el siguiente método que toma
List<Object>
ay agrega anew Object()
.Los genéricos en Java son invariables. A
List<String>
no es aList<Object>
, por lo que lo siguiente generaría una advertencia de compilación:Si hubiera declarado
appendNewObject
tomar un tipo sin formatoList
como parámetro, esto se compilaría y, por lo tanto, perdería la seguridad de tipo que obtiene de los genéricos.Ver también
<E extends Number>
y<Number>
?¿En qué se diferencia un tipo sin formato del uso
<?>
como parámetro de tipo?List<Object>
,List<String>
etc. son todosList<?>
, por lo que puede ser tentador decir que son simplemente en suList
lugar. Sin embargo, hay una gran diferencia: dado que aList<E>
solo defineadd(E)
, no puede agregar cualquier objeto arbitrario a aList<?>
. Por otro lado, dado que el tipo sinList
formato no tiene seguridad de tipo, puedeadd
casi cualquier cosa para aList
.Considere la siguiente variación del fragmento anterior:
¡El compilador hizo un trabajo maravilloso al protegerlo de violar potencialmente la invariancia de tipo del
List<?>
! Si hubiera declarado el parámetro como tipo sin formatoList list
, el código se compilaría y violaría el tipo invariante deList<String> names
.Un tipo sin formato es el borrado de ese tipo
Volver a JLS 4.8:
En términos más simples, cuando se usa un tipo sin formato, los constructores, los métodos de instancia y los no
static
campos también se borran .Tome el siguiente ejemplo:
Cuando usamos el raw
MyType
, tambiéngetNames
se borra, ¡para que devuelva un rawList
!JLS 4.6 continúa explicando lo siguiente:
El siguiente informe de error contiene algunas reflexiones de Maurizio Cimadamore, un desarrollador del compilador, y Alex Buckley, uno de los autores de JLS, sobre por qué debería ocurrir este tipo de comportamiento: https://bugs.openjdk.java.net/browse / JDK-6400189 . (En resumen, simplifica la especificación).
Si no es seguro, ¿por qué está permitido usar un tipo sin formato?
Aquí hay otra cita de JLS 4.8:
Efectivo Java 2nd Edition también tiene esto para agregar:
En resumen, los tipos sin formato NUNCA deben usarse en un código nuevo. Siempre debe usar tipos parametrizados .
¿No hay excepciones?
Desafortunadamente, debido a que los genéricos de Java no están reificados, hay dos excepciones en las que se deben usar tipos sin formato en el nuevo código:
List.class
, noList<String>.class
instanceof
operando, por ejemploo instanceof Set
, noo instanceof Set<String>
Ver también
Collection<String>.class
ilegal?fuente
o instanceof Set<?>
también permite evitar el tipo sin formato (aunque en este caso solo es superficial).n
beans remotos para cada clase de implementación con código idéntico.TypeName.class
, dondeTypeName
es un identificador simple ( jls ). Hablando hipotéticamente, supongo que realmente podría ser cualquiera. Tal vez como una pista,List<String>.class
es la variante que JLS llama específicamente un error del compilador, por lo que si alguna vez lo agregan al lenguaje, esperaría que ese sea el que usan.Los tipos sin procesar son historia antigua del lenguaje Java. Al principio hubo
Collections
y no teníanObjects
nada más y nada menos. Cada operación enCollections
los moldes requeridos delObject
tipo deseado.Si bien esto funcionó la mayor parte del tiempo, ocurrieron errores
Las antiguas colecciones sin tipo no podían hacer cumplir la seguridad de tipo, por lo que el programador tuvo que recordar lo que almacenaba dentro de una colección.
Los genéricos se inventaron para evitar esta limitación, el desarrollador declararía el tipo almacenado una vez y el compilador lo haría en su lugar.
Para comparacion:
Más compleja es la interfaz Comparable:
Tenga en cuenta que es imposible implementar la
CompareAble
interfaz concompareTo(MyCompareAble)
tipos sin formato. Por qué no deberías usarlos:Object
almacenado en unCollection
debe ser lanzado antes de poder ser utilizado.Object
Lo que hace el compilador: los genéricos son compatibles con versiones anteriores, usan las mismas clases de Java que los tipos sin formato. La magia ocurre principalmente en tiempo de compilación.
Será compilado como:
Este es el mismo código que escribiría si utilizara los tipos sin formato directamente. Aunque no estoy seguro de lo que sucede con la
CompareAble
interfaz, creo que crea doscompareTo
funciones, una toma unaMyCompareAble
y la otra toma unaObject
y se la pasa a la primera después de lanzarla.¿Cuáles son las alternativas a los tipos sin procesar: usar genéricos
fuente
Un tipo sin formato es el nombre de una clase o interfaz genérica sin ningún tipo de argumento. Por ejemplo, dada la clase genérica Box:
Para crear un tipo parametrizado de
Box<T>
, debe proporcionar un argumento de tipo real para el parámetro de tipo formalT
:Si se omite el argumento de tipo real, crea un tipo sin formato de
Box<T>
:Por lo tanto,
Box
es el tipo sin formato del tipo genéricoBox<T>
. Sin embargo, una clase no genérica o tipo de interfaz no es un tipo sin formato.Los tipos sin formato aparecen en el código heredado porque muchas clases de API (como las clases de Colecciones) no eran genéricas antes de JDK 5.0. Cuando se usan tipos sin procesar, esencialmente se obtiene un comportamiento pre-genérico: a
Box
daObject
s. Por compatibilidad con versiones anteriores, se permite asignar un tipo parametrizado a su tipo sin formato:Pero si asigna un tipo sin formato a un tipo parametrizado, recibirá una advertencia:
También recibe una advertencia si usa un tipo sin procesar para invocar métodos genéricos definidos en el tipo genérico correspondiente:
La advertencia muestra que los tipos sin procesar omiten las comprobaciones de tipos genéricos, aplazando la captura de código inseguro en tiempo de ejecución. Por lo tanto, debe evitar el uso de tipos sin formato.
La sección de borrado de tipo tiene más información sobre cómo el compilador de Java usa tipos sin formato.
Mensajes de error no verificados
Como se mencionó anteriormente, al mezclar código heredado con código genérico, puede encontrar mensajes de advertencia similares a los siguientes:
Esto puede suceder cuando se usa una API anterior que funciona con tipos sin formato, como se muestra en el siguiente ejemplo:
El término "desmarcado" significa que el compilador no tiene suficiente información de tipo para realizar todas las verificaciones de tipo necesarias para garantizar la seguridad de tipo. La advertencia "sin marcar" está deshabilitada, de forma predeterminada, aunque el compilador da una pista. Para ver todas las advertencias "sin marcar", vuelva a compilar con -Xlint: sin marcar.
Volver a compilar el ejemplo anterior con -Xlint: sin marcar revela la siguiente información adicional:
Para deshabilitar completamente las advertencias no verificadas, use el indicador -Xlint: -unchecked. La
@SuppressWarnings("unchecked")
anotación suprime las advertencias no verificadas. Si no está familiarizado con la@SuppressWarnings
sintaxis, vea Anotaciones.Fuente original: Tutoriales de Java
fuente
Un tipo "en bruto" en Java es una clase que no es genérica y trata con objetos "en bruto", en lugar de parámetros de tipo genérico con seguridad de tipo.
Por ejemplo, antes de que los genéricos de Java estuvieran disponibles, usaría una clase de colección como esta:
Cuando agrega su objeto a la lista, no le importa qué tipo de objeto es, y cuando lo obtiene de la lista, debe convertirlo explícitamente al tipo que espera.
Usando genéricos, elimina el factor "desconocido", porque debe especificar explícitamente qué tipo de objetos pueden ir en la lista:
Tenga en cuenta que con los genéricos no tiene que emitir el objeto proveniente de la llamada get, la colección está predefinida para funcionar solo con MyObject. Este mismo hecho es el principal factor impulsor de los genéricos. Cambia una fuente de errores de tiempo de ejecución en algo que se puede verificar en tiempo de compilación.
fuente
?
todavía ofrece seguridad de tipo. Lo cubrí en mi respuesta.Debe especificar el tipo-parámetro.
La advertencia informa que los tipos definidos para admitir genéricos deben ser parametrizados, en lugar de usar su forma sin formato.
List
se define a los genéricos de apoyo:public class List<E>
. Esto permite muchas operaciones de tipo seguro, que se verifican en tiempo de compilación.fuente
private static List<String> list = new ArrayList<>();
¿Qué es un tipo sin formato y por qué a menudo escucho que no deberían usarse en un código nuevo?
Un "tipo sin formato" es el uso de una clase genérica sin especificar un argumento de tipo (s) para su tipo (s) parametrizado, por ejemplo, usando en
List
lugar deList<String>
. Cuando se introdujeron los genéricos en Java, se actualizaron varias clases para usar los genéricos. El uso de estas clases como un "tipo sin formato" (sin especificar un argumento de tipo) permitió que el código heredado aún se compilara.Los "tipos sin formato" se utilizan para la compatibilidad con versiones anteriores. No se recomienda su uso en código nuevo porque el uso de la clase genérica con un argumento de tipo permite una escritura más fuerte, lo que a su vez puede mejorar la comprensión del código y conducir a detectar problemas potenciales antes.
¿Cuál es la alternativa si no podemos usar tipos sin formato y cómo es mejor?
La alternativa preferida es utilizar clases genéricas según lo previsto, con un argumento de tipo adecuado (por ejemplo
List<String>
). Esto permite que el programador especifique tipos más específicamente, transmite más significado a los futuros mantenedores sobre el uso previsto de una estructura de datos o variable, y permite al compilador hacer cumplir una mejor seguridad de tipos. Estas ventajas juntas pueden mejorar la calidad del código y ayudar a prevenir la introducción de algunos errores de codificación.Por ejemplo, para un método donde el programador quiere asegurarse de que una variable de Lista llamada 'nombres' contenga solo Cadenas:
fuente
polygenelubricants
las referencias de "tipo sin formato " de stackoverflow.com/questions/2770111/… en mi propia respuesta, pero supongo que las dejaré para usar en su propia respuesta.El compilador quiere que escribas esto:
porque de lo contrario, podría agregar cualquier tipo que desee
list
, haciendo que la instanciación no tenganew ArrayList<String>()
sentido. Los genéricos de Java son solo una característica de tiempo de compilación, por lo que un objeto creado connew ArrayList<String>()
felicidad aceptará elementosInteger
oJFrame
elementos si se les asigna una referencia del "tipo sin formato"List
: el objeto en sí mismo no sabe nada sobre los tipos que se supone que debe contener, solo el compilador lo sabe.fuente
Aquí estoy considerando múltiples casos a través de los cuales puedes aclarar el concepto
Caso 1
ArrayList<String> arr
es unaArrayList
variable de referencia con tipoString
que hace referencia a unArralyList
Objeto de tipoString
. Significa que solo puede contener objetos de tipo cadena.Es estricto
String
no un tipo sin procesar, por lo que nunca generará una advertencia.Caso 2
En este caso
ArrayList<String> arr
es un tipo estricto pero su objetonew ArrayList();
es un tipo sin formato.Aquí
arr
hay un tipo estricto. Por lo tanto, generará un error de tiempo de compilación al agregar ainteger
.Caso 3
En este caso
ArrayList arr
es un tipo sin formato, pero su objetonew ArrayList<String>();
es un tipo estricto.Agregará cualquier tipo de objeto porque
arr
es un tipo sin formato.fuente
Un tipo sin formato es la falta de un parámetro de tipo cuando se usa un tipo genérico.
El tipo sin formato no se debe usar porque podría causar errores de tiempo de ejecución, como insertar una
double
en lo que se suponía que era unaSet
deint
s.Al recuperar las cosas del
Set
, no sabes lo que está saliendo. Supongamos que espera que sea todoint
s, lo está emitiendoInteger
; excepción en tiempo de ejecución cuandodouble
llega el 3.45.Con un parámetro de tipo agregado a su
Set
, obtendrá un error de compilación de inmediato. Este error preventivo le permite solucionar el problema antes de que algo explote durante el tiempo de ejecución (ahorrando así tiempo y esfuerzo).fuente
Aquí hay otro caso donde los tipos crudos te morderán:
Como se mencionó en la respuesta aceptada, pierde todo el soporte para genéricos dentro del código de tipo sin formato. Cada parámetro de tipo se convierte en su borrado (que en el ejemplo anterior es justo
Object
).fuente
Lo que está diciendo es que tu
list
es unList
objeto no especificado. Es decir, Java no sabe qué tipo de objetos están dentro de la lista. Luego, cuando desee iterar la lista, debe convertir cada elemento para poder acceder a las propiedades de ese elemento (en este caso, Cadena).En general, es una mejor idea parametrizar las colecciones, para que no tenga problemas de conversión, solo podrá agregar elementos del tipo parametrizado y su editor le ofrecerá los métodos adecuados para seleccionar.
fuente
página de ayuda .
Un tipo sin formato es el nombre de una clase o interfaz genérica sin ningún tipo de argumento. Por ejemplo, dada la clase genérica Box:
Para crear un tipo parametrizado de Box, debe proporcionar un argumento de tipo real para el parámetro de tipo formal T:
Si se omite el argumento de tipo real, crea un tipo sin formato de Box:
fuente
Evitar tipos crudos
Por ejemplo ,
Una lista es un tipo sin formato, mientras que
List<String>
es un tipo parametrizado.Cuando se introdujeron los genéricos en JDK 1.5, los tipos sin formato se conservaron solo para mantener la compatibilidad con versiones anteriores de Java. Aunque todavía es posible usar tipos sin formato,
Deben evitarse :
Son menos expresivos y no se documentan de la misma manera que los tipos parametrizados Ejemplo
Para referencia : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
fuente
Encontré esta página después de hacer algunos ejercicios de muestra y tener exactamente el mismo desconcierto.
============== Pasé de este código como lo proporciona la muestra ===============
====================== A este código ========================
================================================== =============================
Puede ser más seguro, pero tardó 4 horas en desmantelar la filosofía ...
fuente
Los tipos sin procesar están bien cuando expresan lo que quieres expresar.
Por ejemplo, una función de deserialización puede devolver a
List
, pero no conoce el tipo de elemento de la lista. Entonces,List
es el tipo de retorno apropiado aquí.fuente