2012-01-03 26 views
13

Si un objeto String es inmutable (y obviamente no puede cambiar su longitud), ¿por qué length() es un método, en lugar de simplemente public final int length como el que hay en una matriz?¿Por qué String.length() es un método?

¿Es simplemente un método getter, o hace algún tipo de cálculo?

Solo estoy tratando de ver la lógica detrás de esto.

+6

¿Por qué no miras la fuente y lo averiguas? – skaffman

+1

¿Qué beneficio obtendría si fuera un campo? Por el contrario, ¿por qué no preguntaste por qué las matrices tienen un campo 'length' en lugar de un método' length() '? – erickson

+0

Las clases que se remontan a las primeras versiones del JDK no muestran mucha consistencia: las cadenas tienen un método 'length()', las matrices tienen un campo, Collections tiene 'size()', ni exponen los getters JavaBeans para éstas (mientras que DOM 'NodeList' sí lo hace). Apuntaría esto a una verruga de diseño preservada por el bien de la compatibilidad, pero realmente no puede respaldar la opinión con nada. – millimoose

Respuesta

16

Java es un estándar, no solo una implementación. Diferentes proveedores pueden licenciar e implementar Java de forma diferente, siempre que se adhieran al estándar. Al hacer la llamada estándar para un campo, eso limita la implementación bastante severamente, sin una buena razón.

También un método es mucho más flexible en términos del futuro de una clase. Casi nunca se hace, excepto en algunas clases muy tempranas de Java, para exponer una constante final como un campo que puede tener un valor diferente con cada instancia de la clase, más que como un método.

El método length() bien predates la interfaz CharSequence, probablemente desde su primera versión. Mira qué bien funcionó. Años más tarde, sin ninguna pérdida de compatibilidad con versiones anteriores, se presentó la interfaz CharSequence y encajó muy bien. Esto no hubiera sido posible con un campo.

Así que realmente invierta la pregunta (que es lo que debe hacer cuando diseña una clase que permanecerá sin cambios durante décadas): ¿Qué gana un campo aquí, por qué no simplemente convertirlo en un método?

+1

Eso es un pensamiento interesante. Un campo, para mí, es menos código con un rendimiento equivalente o mejor. – Acidic

+4

Con una compensación, de menos encapsulación. No puede existir como parte de una interfaz. (Las interfaces tienen constantes, pero eso no es lo mismo) y no puedes cambiar de opinión sobre el campo. De todos modos, cualquier supuesto beneficio de rendimiento es una optimización prematura. http://en.wikiquote.org/wiki/Donald_Knuth – Yishai

+0

"... no puedes cambiar de opinión ..." Pero si un 'String' es inmutable, ¿qué otra cosa puede indicar su número de caracteres por otro que no sea' miembro final'? – Acidic

2

Cadena está usando encapsulación para ocultar sus detalles internos. Un objeto inmutable sigue siendo libre de tener valores internos variables siempre que su estado visible externamente no cambie. La longitud se puede calcular de forma diferida. Te animo a echar un vistazo como el código fuente de String.

+3

No veo cómo crear un método getter en una variable supuestamente 'final' proporciona cualquier encapsulación. – Acidic

+1

La variable final 'count' es una implementación interna que no (y no debe) exponer. Además, la longitud() es especificada por CharSequence, que String implementa. –

+0

Si, por definición, este 'conteo final final 'del que habla siempre es igual al valor de retorno de' longitud() ', usar un método no debería proporcionar beneficios y solo ralentizar el sistema. (hasta que se inserte, si lo hace) – Acidic

1

Comprobando el código fuente de String en Open JDK es solo un captador.

Pero como @SteveKuo señala, esto podría diferir dependiendo de la implementación.

+0

¿Qué significa 'dependiente de la implementación 'en este caso? – Acidic

+3

Quiero decir que la implementación no necesita ser getter para ser compatible con la API. – tidbeck

4

Quizás un método .length() se consideró más coherente con el método correspondiente para un StringBuffer, que obviamente necesitaría más de una variable de miembro final.

La clase String fue probablemente una de las primeras clases definidas para Java. Es posible (y esto es solo una especulación) que la implementación usó un método .length() antes de que existieran las variables miembro final. No pasaría mucho tiempo antes de que el uso del método estuviera bien integrado en el cuerpo del código de Java existente en ese momento.

+2

Su argumento original contiene algo de agua con 'StringBuffer' en lugar de' StringBuilder'. – erickson

+0

@erickson: ¡Oh, claro! Ha pasado tanto tiempo desde que utilicé un 'StringBuffer' ... gracias. –

+0

Parece lógico, aunque supongo que 'Array' también fue una de las primeras clases en Java y, sin embargo, usa un campo. – Acidic

3

Tal vez porque la longitud() proviene del CharSequence interface. Un método es una abstracción más sensible que una variable si va a tener múltiples implementaciones.

+3

'CharSequence' se introdujo en Java 1.4, mucho después de que se definiera' String'. –

+0

'CharSequence' apareció mucho después de que se definiera la API' String'. Sin embargo, es un buen punto, porque si 'length' fuera un campo, la clase' String' tendría que haber sido adaptada con un método redundante para soportar una interfaz (que especifica solo métodos). Los campos de instancia no están orientados a objetos, y no deberían formar parte de una API pública. Los objetos que los usan, como 'Point' y arrays son antiguos e inconsistentes con el trabajo posterior. – erickson

3

Este es un principio fundamental de la encapsulación.

Parte de la encapsulación es que la clase debe ocultar su implementación de su interfaz (en el sentido "diseño por contrato" de una interfaz, no en el sentido de la palabra clave Java).

Lo que desea es la longitud de la cadena: no debería importar si esto está en caché, calculado, delega en otro campo, etc. Si las personas JDK quieren cambiar la implementación en el futuro, deberían poder hacerlo hacerlo sin tener que volver a compilar.

3

Siempre debe utilizar métodos de acceso en clases públicas en lugar de campos públicos, independientemente de si son finales o no (consulte el elemento 14 en Java efectivo).

Cuando permite que un campo sea accedido directamente (es decir, es público) pierde el beneficio de la encapsulación, lo que significa que no puede cambiar la representación sin cambiar la API (si lo hace, puede romper el código de personas) y puede realizar cualquier acción cuando se accede al campo.

Effective Java proporciona una muy buena regla de oro:

Si una clase es accesible fuera de su paquete, proporcionar métodos de acceso, para preservar la flexibilidad para cambiar la representación interna de la clase. Si una clase pública expone sus campos de datos, se pierde toda esperanza de cambiar su representación, ya que el código del cliente se puede distribuir por todas partes.

Básicamente, se hace de esta manera porque es una buena práctica de diseño hacerlo. Deja espacio para cambiar la implementación de String en una etapa posterior sin romper el código para todos.

1

En la mayoría de las implementaciones actuales de JVM una subcadena hace referencia a la matriz de caracteres de la cadena original de los contenidos y necesita campos de inicio y longitud para definir sus propios contenidos, por lo que el método length() se utiliza como un captador. Sin embargo, esta no es la única forma posible de implementar String.

En una implementación posible diferente cada cadena podría tener su propia matriz de caracteres y dado que las matrices de caracteres ya tienen un campo de longitud con la longitud correcta sería redundante tener una para el objeto String, ya que String.length() es un método que no aplicamos Tengo que hacer eso y solo puedo hacer referencia a la matriz interna.length.

Estas son dos posibles implementaciones de String, ambas con sus partes buenas y malas, y pueden reemplazarse entre sí porque el método length() oculta dónde está almacenada la longitud (matriz interna o campo propio).

+0

Por curiosidad, ¿qué pasaría si una implementación tuviera un tipo 'String' que simplemente incluyera' char [] 'y' int' (para 'hashCode'), un tipo' Tailstring' que derivara de 'String', e incluyó un 'StartIndex', un tipo' HeadString' que derivaba de una cadena e incluía un 'PartialLength', y un tipo' SubString' que derivaba de 'String' e incluía ambos? Supongo que hay demasiados códigos para ver si algo de tipo * es * 'String' en lugar de ver si es' instanceOf String' para que funcione, pero parece que de alguna manera ofrece lo mejor de todos los mundos posibles. – supercat

+0

@supercat código de comprobación de Cadena sería un problema. Entonces hay una pequeña penalización de rendimiento para las llamadas virtuales, el jvm tiene que averiguar en tiempo de ejecución si tiene que llamar a String.length(), TailString.length(), HeadString.length() o Substring.length() en lugar de solo llamando a String.length() directamente (esto hace que las optimizaciones de JIT sean más difíciles), por lo que puede tratarse de una decisión de rendimiento frente a huella de memoria. – josefx

+0

Bueno, obviamente Java es lo que es, pero me pregunto cuáles hubieran sido las consecuencias de tener 'string' como un tipo reconocido por el compilador, cuyos contenidos se encapsularían en un' HeapString'. Tal cosa hubiera permitido a los operadores como '==' realizar una comparación de cadenas cuando se usa en el tipo 'string'; las cadenas se pueden convertir a 'Objeto', como con 'Entero', la comparación en cadenas almacenadas como' Objeto' probaría la igualdad de referencia. – supercat

Cuestiones relacionadas