2010-09-22 20 views

Respuesta

5

List<T> es la interfaz, donde ArrayList<T> es clase, y esta clase implementa la interfaz List<T>.

Preferiría el segundo formulario, que es más general, es decir, si no utiliza métodos específicos para ArrayList<T> puede declarar su tipo como tipo de interfaz List<T>. Con la segunda forma, será más fácil cambiar la implementación de ArrayList<T> a otra clase que implemente la interfaz List<T>.

EDIT: Como muchos de SO usuarios comentados ambas formas pueden ser argumentos a cualquier método que acepta List<T> o ArrrayList<T>. Pero cuando declaro método preferiría interfaz:

void showAll(List <String> sl) ... 

y uso:

void showAllAS(ArrayList <String> sl) ... 

sólo cuando mi método utiliza el método específico para ArrayList<T>, como ensureCapacity().

La respuesta con la información que debemos usar mecanografió List<T> en vez de apenas List es muy buena (por supuesto si no utilizamos Java antiguo).

+1

Esto suena redactado al revés (mal) para mí. El primer formulario 'ArrayList aList' se puede pasar como argumento a cualquier método que tome un parámetro' List' o 'ArrayList'. El segundo formulario 'List aList' se puede pasar como un argumento solo a los métodos que toman un parámetro' List'. Aunque la variable 'List aList' hace referencia a' ArrayList', una tipificación fuerte evitaría que la variable de referencia 'aList' coincida como argumento con un parámetro' ArrayList'. –

+2

-1 Es exactamente al revés; puede pasar un 'ArrayList' a los métodos que quieran una' Lista'. No puede pasar una 'Lista' a un método que pida una' ArrayList'. – Ishtar

+3

@Ishtar, Bert, exactamente. Pero olvidó mencionar que un método casi nunca debería esperar una ArrayList. En el 99% de los casos, cualquier lista y en 50% cualquier colección servirá. –

21

La lista es una interfaz, mientras que ArrayList es una implementación de esa interfaz.

La segunda es mejor porque significa que puede cambiar su ArrayList para otra implementación de List más adelante sin necesidad de cambiar el resto de su aplicación. Es posible que desee hacerlo por motivos de rendimiento o debido a otros aspectos del comportamiento de la implementación de la Lista que ha elegido/elegirá.

+0

Exactamente. Si alguna vez encuentra que necesita asegurarse de que está trabajando con un ArrayList más tarde, puede cambiar la declaración a continuación.De lo contrario, el resto de su código realmente no necesita saber qué tipo de lista se está utilizando, de modo que puede intercambiar para una implementación diferente más tarde (tal vez LinkedList) si el rendimiento u otras consideraciones lo justifican. –

+5

@ Bill todos siempre mencionan LinkedList en la implementación que también puede cambiar, pero creo que el caso se fortalece si menciona Collections.unmodifiableList(), Collections.checkedList() o Collections.synchronizedList(). – ILMTitan

+0

@ILMTitan por no mencionar otras colecciones como Inmutable de Google Collections. – Jorn

1

Prefiero el segundo en la mayoría de los casos porque significa que no está utilizando nada específico en la API de ArrayList, y si lo necesita más adelante puede sustituir cualquier otro tipo de lista sin tener que cambiar ningún código excepto el primero línea.

7

Ambos están en desuso desde Java 1.5.

Debería ser:

List<String> list = new ArrayList<String>(); 
// or whatever data type you are using in your list 

favor, lea Effective Java por Joshua Bloch, especialmente en estos dos artículos:

  • 23: No utilice los tipos de primas en el nuevo código (esto es aún available online)
  • 52: Consulte objetos por sus interfaces de

Por cierto, si se utiliza Guava, tiene a factory method para la construcción de un ArrayList por lo que no tiene que repetir el parámetro de tipo:

List<String> list = Lists.newArraylist(); 
+0

Espero que todos sepan que ya deben usar la versión de plantilla de List (por ejemplo, eclipse le molestará si no lo hace). ¡Pero tu segundo comentario sobre la guayaba es bastante interesante! – Roalt

+1

+1 para "Java efectivo", es un libro realmente bueno. – dertoni

+0

@dertoni Estoy de acuerdo, es el único * uno * libro que todo desarrollador de Java debe leer. –

0

Si está utilizando Java 1.4 o anterior, entonces me gustaría utilizar la segunda uno.Siempre es mejor declarar sus campos tan genérica como sea posible, en caso de tener que hacerlo en otra cosa más adelante, como un vector, etc.

Desde 1.5 Me gustaría ir con la siguiente

List<String> x = new ArrayList<String>(); 

Te da un poco de seguridad tipo. La lista 'x' puede agregar un objeto String, y eso es todo. Cuando obtiene un artículo de la Lista 'x', puede contar con el hecho de que una Cadena va a regresar. Esto también ayuda eliminando el casting innecesario, lo que hace que el código sea difícil de leer cuando regresas 6 meses después e intentas recordar qué hace tu código. Su compilador/IDE le ayudará a recordar qué tipo debe entrar en la Lista 'x' mostrando un error si intenta agregar cualquier otro tipo de Objeto.

Si desea añadir varios tipos de objetos a una lista entonces se podría añadir una anotación para suprimir el error de compilación

@SuppressWarnings("unchecked") 
List y = new ArrayList(); 
+1

Esto es un mal estilo. No suprima las advertencias, use 'List '. Ejemplo: 'List y = new ArrayList (); y.add (1); y.add ("abc"); y.add (true); ' –

+0

(El auto-boxing que utilicé en mi ejemplo también es de mal estilo, por supuesto) –

+1

Si está utilizando 1.4 o una versión anterior, lo primero que debe hacer es actualizar su JVM. –

0

Sé de al menos un caso cuando se declara una variable con una interfaz no funciona . Cuando quieres usar la reflexión

Hice una corrección de error en algún código donde declare una variable como Map<String, MyObject> y le asigné una instancia de HashMap<String, MyObject>. Esta variable se usó como parámetro en una llamada a método a la que se accedió mediante reflexión. El problema es que la reflexión intentó encontrar un método con la firma HashMap y no la firma declarada Map. Como no había ningún método con un HashMap como parámetro, no pude encontrar un método por reflexión.

Map<String, Object> map = new HashMap<String, Object>(); 

public void test(Map<String, Object> m) {...}; 

Method m = this.getClass().getMethod("test", new Class<?>[]{map.getClass()}); 

Will no encontrar el método que utiliza la interfaz. Si realiza otra versión de prueba que usa HashMap, funcionará, pero ahora está obligado a declarar su variable con una clase concreta y no con la interfaz más flexible ...

+3

el error está en el código de búsqueda de método. una búsqueda sofisticada no requerirá una concordancia exacta del tipo de parámetro; en su lugar, simula el algoritmo del compilador para encontrar un método adecuado. – irreputable

+0

El error real aquí es que estás usando map.getClass(), que devolverá HashMap.class, para la búsqueda del método, mientras que deberías usar Map.class en su lugar. – Jorn

+0

@John parece un pequeño error tipográfico, pero es más que eso. Todo el propósito detrás de usar la reflexión en este caso particular es que NO sé qué método llamar. Me pasaron una lista para los parámetros. Si supiera que quería usar el método con el parámetro Map <>, no usaría reflection :) – BigMac66

1

Todas estas respuestas son las mismas recitar algún dogma que leen en alguna parte.

Una declaración de variable y declaración de inicialización, Type x = new Constructor();, es definitivamente parte del detalle de implementación. No es parte de una API pública (a menos que sea public final, pero List es mutable, por lo que es inapropiado)

Como detalle de implementación, ¿a quién intenta engañar con sus abstracciones? Es mejor mantener el tipo lo más específico posible. ¿Es una lista de arreglos o una lista vinculada? ¿Debería ser seguro o no? La elección es importante para su implementación, usted eligió cuidadosamente la lista específica impl. Entonces lo declaras como un mero List como si no importara y no te importa?

La única razón legítima para declararlo como List es Soy demasiado flojo para escribir. Eso también cubre el argumento de que si necesito moverme a otra lista impl, tengo un lugar menos para modificar.

Este motivo es legítimo solo cuando el alcance de la variable es pequeño y puede ver todos sus usos de un solo vistazo. De lo contrario, conserve el tipo más específico, de modo que su rendimiento y características semánticas se manifiesten en todo el código que usa la variable.

+0

En general estoy en desacuerdo con esto, pero no se merece -3; votando porque plantea argumentos válidos (aunque estoy en desacuerdo con la conclusión). – sleske

+0

Por cierto, un punto en el que no estoy de acuerdo es "La elección es importante para su implementación, usted eligió cuidadosamente la lista específica impl" Si ese es el caso, usar el tipo concreto puede estar justificado, pero en mi experiencia, a menudo no es el caso. Cuando v.g. usando clases de colección, casi siempre solo uso ArrayList & HashSet, y solo reconsidero si eso causa problemas (es decir, prácticamente nunca). En ese caso, usar List/Set mejor expresa mi * intención *, es decir, "solo quiero una lista". – sleske

+0

+1; Estoy totalmente de acuerdo en eso. Si el alcance es pequeño, utilice el tipo específico. Me arrojaron aquí de su otra respuesta: http://stackoverflow.com/a/12805778/1350762 – maba

1

Esto es una peculiaridad de Java, debido a la inferencia de tipo limitado y doctrina OOP.Para las variables locales, es principalmente estilo; si necesita alguna característica específica del subtipo, use el subtipo (ejemplos a continuación), pero de lo contrario puede usar cualquiera de los dos.

estilo de Java es utilizar el supertipo, para hacer cumplir las interfaces incluso dentro del conjunto de realizaciones, y por coherencia con los tipos visibles (Effective Java 2ª Edición: Artículo 52: se refieren a objetos mediante sus interfaces). En los idiomas con más inferencia de tipo, como C++/C#/Go/etc., No necesita indicar explícitamente el tipo, y la variable local tendrá el tipo específico.

Para los tipos visibles (públicos o protegidos: campos o parámetros y valor de retorno de los métodos), casi siempre desea utilizar el tipo más general: interfaz o clase abstracta, para proporcionar una mejor flexibilidad (Java efectiva: Elemento 40: diseñar firmas de método cuidadosamente). Sin embargo, para los tipos que no son visibles (miembros privados o de paquete privado, o variables locales), está bien usar cualquiera (es solo estilo) y a veces es necesario usar tipos más específicos, incluyendo clases concretas.

Ver Efectivo Java para las pautas estándar; pensamientos personales siguen

La razón para utilizar tipos más generales, incluso si no es visible, es reducir el ruido: indica que solo necesita el tipo más general. El uso de tipos generales en los miembros que no son visibles (como los métodos privados) también reduce el abandono si alguna vez cambia los tipos. Sin embargo, esto no se aplica a las variables locales, donde solo está cambiando una línea de todos modos: ConcreteFoo foo = new ConcreteFoo(); a OtherConcreteFoo foo = new OtherConcreteFoo();.

casos en los que haces necesitan el subtipo incluyen:

  • Es necesario miembros sólo está presente en el subtipo, por ejemplo:
    • alguna característica de la aplicación, como ensureCapacity para ArrayList<T>
    • (común en el código de prueba) algún miembro de una clase falsa, como (hipotéticamente) FakeFileSystem#createFakeFile.
  • Usted confía en el comportamiento del subtipo, especialmente en las anulaciones de los métodos del supertipo, como:
    • tener un tipo más general de los parámetros,
    • tipo de retorno más específica, o
    • lanzando un tipo de excepción más específico o menos.

Como un ejemplo de la última, véase Should I close a StringReader?: StringReader.html#close anulaciones Reader.html#close y hace no lanzar una IOException, por lo que usar en lugar de StringReaderReader para la variable local significa que no es necesario para manejar una excepción que en realidad no puede ocurrir, y reduce significativamente el texto repetitivo.

Cuestiones relacionadas