2010-08-31 9 views
7

Esto puede sonar muy básico. Pero soy nuevo para Java. Con las pocas horas iniciales de aprendizaje que he puesto en la medida, que estoy continuamente perplejo por la redundancia en la sintaxis de una nueva declaración de objeto:¿Eficiencia de sintaxis de creación de objetos Java?

TypeName a = new TypeName(); 

En particular,

String s = new String("abc"); 
Character c = new Character("A"); 

Por qué en el mundo alguien quisiera escribir la palabra clave TypeName (por ejemplo, String, Character, etc ...) dos veces? Entiendo que hay cortos de manos:

String s = "abc"; 
char c = "A"; 

Pero éstas son excepciones en lugar de reglas. Entonces, ¿alguien puede iluminarme, por favor? Gracias.

+2

Una pregunta en la línea de "¿por qué los diseñadores de idiomas lo hicieron de esa manera?" Probablemente sea mejor preguntada a los diseñadores de idiomas, no a nosotros. –

+0

Pregunto aquí porque a pesar de que no sabía la respuesta, estaba seguro de que se reduciría a una conveniencia de programación (por lo tanto, mejor respondida por los programadores) en lugar de filosofía de diseño sin impacto directo, ¿verdad? – Zhang18

Respuesta

4

Esto no es en absoluto redundante. Hay dos pasos para utilizar una variable:

  1. declaración: este paso le dice a la máquina virtual de lo que será la huella estática de la variable. Por ejemplo: Object a; tendrá solo la huella declarada en la clase Object visible, mientras que Integer b; tendrá toda la huella declarada en la clase Integer y todas las clases primarias heredadas, hasta Object visibles. Esto es para la parte estática.

  2. instanciation: este paso le dice a la máquina virtual cuál será la huella dinámica de la variable. Por ejemplo: List<String> c = new LinkedList<String>();, luego c.put("foo"); usará la implementación LinkedList del método put(), incluso si lo que está visible es List::put(). A veces, requerirá ese tipo de declaración/instanciación, pero deberá sobrescribir para acceder a un método muy específico que no se ve con la huella estática. Por ejemplo, vamos a considerar un método declarado como public void method1(Object obj) y sabe que la instancia obj es en realidad un Integer, por lo tanto se le específicamente utilizar la huella dinámica echando el objeto en él: int value = ((Integer) obj).intValue();

Ahora , en cuanto a la parte String a = "A";. Java ha hecho que la escritura abreviada de las clases "primitivas" esté disponible para simplificar. Más específicamente, desde Java 1.5, usted puede hacer:

Integer n1 = 1; 
Integer n2 = new Integer(1); // same thing 
int n3 = n2; 

Y todas las obras. Pero, ¿cuál es la diferencia? Considere este pedazo de código:

String a = new String("A"); 
String b = new String("A"); 
String c = "A"; 
String d = "A"; 

System.out.println("a:" + a.hashCode() + " = b:" + b.hashCode() + " == " + (a == b)); 
System.out.println("b:" + b.hashCode() + " = c:" + c.hashCode() + " == " + (b == c)); 
System.out.println("c:" + c.hashCode() + " = d:" + d.hashCode() + " == " + (c == d)); 

es la salida

a:65 = b:65 == false 
b:65 = c:65 == false 
c:65 = d:65 == true 

¿Por qué? Como la JVM intenta reutilizar la memoria tanto como sea posible, y dado que a y b están creando nuevas instancias de String, no comparten el mismo espacio de memoria. Sin embargo, c y d están utilizando valores de cadena constantes (esto es una optimización del compilador) y, por lo tanto, están apuntando exactamente al mismo objeto String.

5

Con esta sintaxis puede crear fácilmente un objeto de tipo X y asignarlo a una variable de tipo Y:

List<String> myList = new ArrayList<String>(); 
19

Debido a que a veces uno quiere hacer algo como:

// Explicitly force my object to be null 
String s = null; 

o

// Cast a child class to its parent 
MyParentClass mpc = new IneritedClassFromParent(); 

o

// Store and access a concrete implementation using its interface 
ISomeInterface isi = new ConcreteInterfaceImplementation(); 

En otras palabras, el hecho de que declare un tipo para almacenar no siempre significa que desea que se inicialice con una nueva instancia de esa clase.

Es posible que desee utilizar una nueva instancia de una clase secundaria al usar la herencia o una Implementación de interfaz al usar Interfaces.

O a veces es posible que desee forzar explícitamente que algo sea nulo inicialmente y llenarlo más tarde.

+0

Sin embargo, esto no explica por qué necesita mencionar el tipo dos veces en los ejemplos que dio el OP. La inferencia de tipo ha existido durante casi 100 años, es decir, más larga que los lenguajes de programación y * ciertamente * más larga que Java, así que creo que la pregunta por qué Java falta una característica que ha sido básicamente estándar desde los 80 sigue abierta. –

+0

Estoy de acuerdo con Jorg W Mittag. En particular, tengo una pregunta de seguimiento para los ejemplos que mostró aquí. ¿Por qué nadie puede tener 'InheritedClassFromParent mpc = new InheritedClassFromParent()'? ¿Cuál es el beneficio de declarar la variable en la clase padre? – Zhang18

+1

@ Zhang18 - Es por la misma razón que utilizaría una interfaz. Si el padre define los miembros que necesita, es mejor usar el tipo más genérico. Entonces puede cambiar el tipo subyacente sin romper el código dependiente. Por ejemplo, podría cambiar 'MyParentClass mpc = new InheritedClassFromParent();' a 'MyParentClass mpc = SomeOtherChild();' sin romper el código que usa mpc mientras que con tipos concretos ... podría causar cambios bruscos. –

0

Eso es porque no hay implícita forma de asignar el valor de un objeto complejo.

Al hacer int a = 3; o double b = 2.5;, se puede afirmar implícitamente el tipo en el lado derecho.

En OOP, tiene que usar un constructor por lo que tiene que hacer new TypeName(). Esto también le da la capacidad de pasar parámetros para configurar el objeto.

Otra razón es cuando usa interfaces. Por lo que puede hacer:

MyInterface blah = new InterfaceImplementation(); 
MyInterface bar = new AnotherInterfaceImplementation(); 

e incluso:

ParentClass foo = new DerivedClass(); 

Esto es porque cuando se trabaja con interfaces que normalmente no desea establecer el tipo de variable a la interfaz de la aplicación, pero la interfaz sí mismo. De lo contrario, no habría forma de especificar qué implementación usar.

Otra cosa útil es genéricos:

List<SomeType> myList = new ArrayList<SomeType>(); 

Java 7 simplificará esto a

List<SomeType> myList = new ArrayList<>(); 

de modo que usted no tiene que escribir <SomeType> dos veces (esto especialmente doloroso pone en Mapas).

0

Cuando se llega a los genéricos de clase (extensiones):

class a extends b 

Va a ver que se puede hacer algo como esto:

b=new a(); 
0

Bueno, una variable tiene que tener un tipo . Y cuando crea una instancia de un objeto, necesita indicar qué tipo debería ser. Y por supuesto, estos no tienen que ser lo mismo. Puede establecer una Cadena en una variable Objeto, por ejemplo. Por supuesto, podría tener algo como esto para hacer las cosas un poco más fáciles:

var s = new TypeName(); 

Así es como se ha hecho en C#. Pero supongo que en Java no han visto la necesidad de eso.
Estoy de acuerdo en que Java es bastante detallado para los estándares modernos, pero también es bastante fácil de leer y no hay mucha azúcar sintáctica para confundirte.

1

Muchas buenas respuestas aquí en cuanto a por qué es necesario. Tiene razón en que a menudo parece redundante. java a menudo ha sido (no injustamente) criticado como un poco, er ... detallado. Hay algunos atajos. p.ej. para las cadenas String s="Abc" (no es realmente un atajo, es un poco diferente, y mejor, porque no está creando explícitamente un objeto nuevo). También habrá una reducción de la duplicación en las declaraciones en Java 7 para los genéricos.

0

Hay lenguajes fuertemente tipados que admiten la "inferencia de tipo" (como Scala, por ejemplo). Java simplemente no es uno de ellos (aunque habrá algún tipo de inferencia de argumentos genéricos en Java 7). En estos lenguajes, aunque el tipo de variable no está declarado, el compilador puede inferirlo inequívocamente y aún detectar errores de tipo. Por ejemplo (no Java):

val str = "This is not a number!"; 
val x = str.intValue(); // Compiler error, because str is implicitly a String. 

En Java, habrá muchos casos en los que se le asigna un tipo específico concreto sobre el derecho a un tipo más general de la izquierda. Por ejemplo:

Set students = new TreeSet(); 

Este es un buen estilo, porque su código no dependerá de una implementación específica. Si necesita cambiar implementaciones (por ejemplo, necesita búsquedas más rápidas desde un Set basado en hash), solo cambiará el lado derecho del inicializador.

Por esta razón, es especialmente importante declarar las API públicas utilizando las abstracciones correctas, en lugar de los tipos concretos.

5

¿Por qué en el mundo alguien desearía tipo de la palabra clave TypeName (por ejemplo. String, de caracteres, etc ...) dos veces?

Debido a que estás haciendo dos cosas:

  • declarar una variable de un tipo determinado
  • Creación de un objeto de un cierto tipo

Los dos tipos no son necesariamente las lo mismo, por ejemplo

Map m = new HashMap(); 

Probablemente estés acostumbrado a "tipado dinámico" lenguajes como PHP donde las variables no tienen un tipo. La ventaja que obtienes con las declaraciones de tipo estático de Java es que muchos errores de programación son captados por el compilador (incluso, en un IDE moderno, mientras escribes). Por ejemplo, si se comete un error simple:

m.siez(); 

El compilador le avisará inmediatamente al hecho de que hay algo mal con su programa - y se puede hacer eso sólo porque sabe que el tipo declarado Map hace no tiene un método siez().

Algunos lenguajes modernos tipados estáticos como C# y Scala usan type inference para darle un "mejor de los dos mundos" donde puede omitir la declaración de tipo y el compilador asumirá que es el mismo que el tipo del objeto que está asignando lo. Sin embargo, dichos lenguajes siempre permiten declaraciones explícitas de tipo porque la inferencia de tipo no siempre es posible o deseable (como en el ejemplo anterior donde se supone que la variable usa la interfaz en lugar de la clase concreta).