Todo programador Java competente sabe que necesita usar String.equals () para comparar una cadena, en lugar de == porque == verifica la igualdad de referencia.
Cuando estoy tratando con cadenas, la mayoría de las veces estoy verificando la igualdad de valores en lugar de la igualdad de referencia. Me parece que sería más intuitivo si el lenguaje permitiera comparar los valores de cadena simplemente usando ==.
Como comparación, el operador de C # 's == verifica la igualdad de valores para la cadena s. Y si realmente necesita verificar la igualdad de referencia, puede usar String.ReferenceEquals.
Otro punto importante es que las cadenas son inmutables, por lo que no hay que hacer daño al permitir esta función.
¿Hay alguna razón particular por la que esto no se implementa en Java?
fuente
==
es la igualdad de objetos y laeq
igualdad de referencia ( ofps.oreilly.com/titles/9780596155957/… ).==
operador solo se asigna aEquals
si el==
operador se implementó de esa manera. El comportamiento predeterminado para==
es el mismo queReferenceEquals
(en realidad,ReferenceEquals
se define como la versión del objeto de==
)Respuestas:
Supongo que es solo consistencia, o "principio de mínimo asombro". La cadena es un objeto, por lo que sería sorprendente si se tratara de manera diferente a otros objetos.
En el momento en que salió Java (~ 1995), el simple hecho de tener algo así
String
era un lujo total para la mayoría de los programadores que estaban acostumbrados a representar las cadenas como matrices terminadas en nulo.String
El comportamiento de ahora es lo que era en ese entonces, y eso es bueno; Cambiar sutilmente el comportamiento más adelante podría tener efectos sorprendentes y no deseados en los programas de trabajo.Como nota al margen, podría usar
String.intern()
para obtener una representación canónica (interna) de la cadena, después de lo cual se podrían hacer comparaciones==
. La pasantía lleva algo de tiempo, pero después de eso, las comparaciones serán realmente rápidas.Además: a diferencia de lo que sugieren algunas respuestas, no se trata de soportar la sobrecarga del operador . El
+
operador (concatenación) funciona enString
s aunque Java no admite la sobrecarga del operador; simplemente se maneja como un caso especial en el compilador, resolviendoStringBuilder.append()
. Del mismo modo,==
podría haber sido manejado como un caso especial.Entonces, ¿por qué asombrar con un caso especial
+
pero no con==
? Porque,+
simplemente no se compila cuando se aplica aString
objetos que no son , por lo que eso se hace evidente rápidamente. El comportamiento diferente de==
sería mucho menos evidente y, por lo tanto, mucho más sorprendente cuando te golpea.fuente
James Gosling , el creador de Java, lo explicó de esta manera en julio de 2000 :
fuente
==
operador está sobrecargado para objetos y primitivas. El+
operador está sobrecargado parabyte
,short
,int
,long
,float
,double
,String
y probablemente un par de los demás me olvidó. Hubiera sido perfectamente posible sobrecarga==
paraString
así.Consistencia dentro del lenguaje. Tener un operador que actúe de manera diferente puede ser sorprendente para el programador. Java no permite a los usuarios sobrecargar a los operadores, por lo tanto, la igualdad de referencia es el único significado razonable para
==
entre objetos.Dentro de Java:
==
compara igualdad numérica==
compara la igualdad booleana==
compara identidad de referencia.equals(Object o)
para comparar valoresEso es. Regla simple y simple de identificar lo que quieres. Todo esto está cubierto en la sección 15.21 de la JLS . Comprende tres subsecciones que son fáciles de entender, implementar y razonar.
Una vez que permite la sobrecarga
==
, el comportamiento exacto no es algo en lo que pueda mirar al JLS y poner su dedo en un elemento específico y decir "así es como funciona", el código puede ser difícil de razonar. El comportamiento exacto de==
puede ser sorprendente para un usuario. Cada vez que lo ve, tiene que regresar y verificar para ver qué significa realmente.Dado que Java no permite la sobrecarga de operadores, se necesita una forma de tener una prueba de igualdad de valores que pueda anular la definición base. Por lo tanto, fue obligatorio por estas opciones de diseño.
==
en Java prueba numérico para tipos numéricos, igualdad booleana para tipos booleanos e igualdad de referencia para todo lo demás (que puede anularse.equals(Object o)
para hacer lo que quieran para la igualdad de valores).Este no es un problema de "¿hay un caso de uso para una consecuencia particular de esta decisión de diseño" sino más bien "esta es una decisión de diseño para facilitar estas otras cosas, es una consecuencia de ello".
Cadena interna , es un ejemplo de esto. De acuerdo con JLS 3.10.5 , todos los literales de cadena están internados. Se internan otras cadenas si se invoca
.intern()
en ellas. Eso"foo" == "foo"
es cierto es una consecuencia de las decisiones de diseño tomadas para minimizar la huella de memoria ocupada por los literales de cadena. Más allá de eso, el internamiento de cadenas es algo que está en el nivel JVM que tiene un poco de exposición al usuario, pero en la abrumadora gran mayoría de los casos, no debería ser algo que le preocupe al programador (y los casos de uso para programadores no fueron algo que ocupaba un lugar destacado en la lista para los diseñadores al considerar esta función).La gente lo señalará
+
y+=
se sobrecargará por String. Sin embargo, eso no es ni aquí ni allá. Sigue siendo el caso de que si==
tiene un valor de igualdad de significado para String (y solo String), uno necesitaría un método diferente (que solo existe en String) para la igualdad de referencia. Además, esto complicaría innecesariamente los métodos que toman Object y esperan==
comportarse de una manera y.equals()
comportarse de otra manera, lo que requiere que los usuarios hagan un caso especial de todos esos métodos para String.El contrato consistente para los
==
Objetos es que solo es igualdad de referencia y que.equals(Object o)
existe para todos los objetos que deben probar la igualdad de valor. Para complicar esto, se complican demasiadas cosas.fuente
new String("foo") == new String("foo")
puede ser cierto (ver Desduplicación de cadenas ).Java no admite la sobrecarga del operador, lo que significa que
==
solo se aplica a tipos primitivos o referencias. Cualquier otra cosa requiere la invocación de un método. Por qué los diseñadores hicieron esto es una pregunta que solo ellos pueden responder. Si tuviera que adivinar, probablemente sea porque la sobrecarga del operador trae complejidad que no estaban interesados en agregar.No soy un experto en C #, pero los diseñadores de ese lenguaje parecen haberlo configurado de manera tal que cada primitivo es un objeto
struct
y cada unostruct
es un objeto. Debido a que C # permite la sobrecarga del operador, esa disposición hace que sea muy fácil para cualquier clase, no soloString
, trabajar de la manera "esperada" con cualquier operador. C ++ permite lo mismo.fuente
==
.==
. Eso está agregando efectivamente la sobrecarga del operador, lo que tendría enormes implicaciones en la forma en que se implementa Java.ClassName.ReferenceEquals(a,b)
), y un==
operador yEquals
método predeterminados que apuntan a ambosReferenceEquals
.Esto se ha hecho diferente en otros idiomas.
En Object Pascal (Delphi / Free Pascal) y C #, el operador de igualdad se define para comparar valores, no referencias, cuando se opera en cadenas.
Particularmente en Pascal, la cadena es un tipo primitivo (una de las cosas que realmente me encanta de Pascal, obtener NullreferenceException solo por una cadena no inicializada es simplemente irritante) y tener una semántica de copia en escritura, lo que hace (la mayoría de las veces) operaciones de cadena muy barato (en otras palabras, solo se nota una vez que comienza a concatenar cadenas de varios megabytes).
Entonces, es una decisión de diseño de lenguaje para Java. Cuando diseñaron el lenguaje, siguieron el camino de C ++ (como Std :: String), por lo que las cadenas son objetos, lo que en mi humilde opinión es un truco para compensar que C carezca de un tipo de cadena real, en lugar de hacer que las cadenas sean primitivas (que son).
Entonces, por una razón, solo puedo especular que hicieron que sea fácil de su parte y no codificar el operador hace una excepción en el compilador de cadenas.
fuente
String
debería haber sido un tipo primitivo en Java. A diferencia de otros tipos, el compilador necesita saber sobreString
; Además, las operaciones en él serán lo suficientemente comunes como para que muchos tipos de aplicaciones puedan presentar un cuello de botella de rendimiento (que podría ser facilitado por el soporte nativo). Un típicostring
[en minúsculas] tendría un objeto asignado en el montón para mantener su contenido, pero no existiría ninguna referencia "normal" a ese objeto en ninguna parte; Por lo tanto, podría ser un solo indirectoChar[]
o, enByte[]
lugar de tener que serChar[]
indirectamente, a través de otro objeto.En Java, no hay sobrecarga de operadores en absoluto, y es por eso que los operadores de comparación solo están sobrecargados para los tipos primitivos.
La clase 'String' no es primitiva, por lo tanto no tiene una sobrecarga para '==' y usa el valor predeterminado de comparar la dirección del objeto en la memoria de la computadora.
No estoy seguro, pero creo que en Java 7 u 8 Oracle hizo una excepción en el compilador para reconocer
str1 == str2
comostr1.equals(str2)
fuente
s1
ys2
les doy el mismo contenido, pasan las1.equals(s2)
comparación de igualdad ( ) pero no la misma referencia (==
) porque son dos objetos diferentes. Cambiar la semántica de==
significa igualdad significarías1 == s2
evaluartrue
dónde solía evaluarfalse
.Java parece haber sido diseñado para mantener una regla fundamental de que el
==
operador debe ser legal cada vez que un operando se puede convertir al tipo del otro, y debe comparar el resultado de dicha conversión con el operando no convertido.Esta regla no es exclusiva de Java, pero tiene algunos efectos de largo alcance (y desafortunadamente en mi humilde opinión) en el diseño de otros aspectos del lenguaje relacionados con el tipo. Hubiera sido más claro especificar los comportamientos
==
con respecto a combinaciones particulares de tipos de operandos, y prohibir combinaciones de tipos X e Y dondex1==y1
yx2==y1
no implicaríanx1==x2
, pero los idiomas rara vez hacen eso [bajo esa filosofía,double1 == long1
o tendrían que indicar sidouble1
no es una representación exactalong1
o se niega a compilar;int1==Integer1
debería estar prohibido, pero debería haber un medio conveniente y eficiente de no arrojar para probar si un objeto es un entero en caja con un valor particular (la comparación con algo que no es un entero en caja debería simplemente devolverfalse
)].Con respecto a la aplicación del
==
operador a las cadenas, si Java hubiera prohibido las comparaciones directas entre operandos de tipoString
yObject
, podría haber evitado sorpresas en el comportamiento de==
, pero no hay un comportamiento que podría implementar para tales comparaciones que no sería sorprendente. Tener dos referencias de cadena mantenidas en tipo seObject
comportan de manera diferente a las referencias mantenidas en tipoString
habría sido mucho menos sorprendente que tener cualquiera de esos comportamientos diferentes de los de una comparación legal de tipo mixto. SiString1==Object1
es legal, eso implicaría que la única forma de que los comportamientosString1==String2
y lasObject1==Object2
coincidenciasString1==Object1
sea que coincidan entre sí.fuente
==
en los objetos simplemente debería llamar a (null-safe) igual y algo más (por ejemplo,===
oSystem.identityEqual
) debería usarse para la comparación de identidad. Inicialmente, se prohibiría mezclar primitivas y objetos (no había autoboxing antes de 1.5) y luego se podría encontrar una regla simple (por ejemplo, unbox seguro nulo, luego emitir, luego comparar).int==Integer
operador devolvierafalse
si elInteger
valor es nulo y, de lo contrario, compare valores, ese enfoque habría sido diferente al comportamiento de==
en todas las demás circunstancias, donde incondicionalmente obliga a ambos operandos al mismo tipo antes comparándolos. Personalmente, me pregunto si se implementó el desempaquetado automático en un esfuerzo por permitirint==Integer
tener un comportamiento que no tenía sentido ...int
y hacer una comparación de referencia hubiera sido una tontería [pero no siempre fallaría]. De lo contrario, no veo ninguna razón para permitir una conversión implícita que pueda fallar con un NPE.==
no tiene nada que veridentityEquals
. +++ "operadores de igualdad separados para el valor y la igualdad de referencia", pero ¿cuáles? Me gustaría considerar tanto primitiva==
yequals
como hacer valor de comparación en el sentido de queequals
se ve en el valor de la referencia. +++ Cuando se==
quiere decirequals
,int==Integer
DEBE hacer autoboxing y comparar las referencias usando iguales nulos seguros. +++ Me temo que mi idea no es realmente mía, sino lo que hace Kotlin.==
nunca se probó la igualdad de referencia, entonces podría realizar una prueba de igualdad de valores segura y nula. El hecho de que se hace la igualdad de referencia de la prueba, sin embargo, limita seriamente la forma en que puede manejar las comparaciones de referencia / valor mixtos sin inconsistencia. Tenga en cuenta también que Java se fija en la noción de que los operadores promueven ambos operandos al mismo tipo, en lugar de generar comportamientos especiales basados en las combinaciones de tipos involucrados. Por ejemplo,16777217==16777216.0f
regresatrue
porque realiza una conversión con pérdida del primer operando afloat
, mientras que un ...En general, hay muy buenas razones para querer probar si dos referencias de objeto apuntan al mismo objeto. He tenido muchas veces que he escrito
Puedo o no tener una función igual en tales casos. Si lo hago, la función igual puede comparar todo el contenido de ambos objetos. A menudo solo compara algún identificador. "A y B son referencias al mismo objeto" y "A y B son dos objetos diferentes con el mismo contenido" son, por supuesto, dos ideas muy diferentes.
Probablemente sea cierto que para objetos inmutables, como Strings, esto no es un problema. Con los objetos inmutables, tendemos a pensar que el objeto y el valor son lo mismo. Bueno, cuando digo "nosotros", me refiero a "yo", al menos.
Por supuesto, eso devuelve falso, pero puedo ver a alguien pensando que debería ser cierto.
Pero una vez que diga que == compara los identificadores de referencia y no el contenido de los Objetos en general, hacer un caso especial para las Cadenas sería potencialmente confuso. Como alguien más dijo aquí, ¿qué pasaría si quisieras comparar los controladores de dos objetos String? ¿Habría alguna función especial para hacerlo solo para cadenas?
Y que hay con ...
¿Es falso porque son dos objetos diferentes o verdadero porque son cadenas cuyo contenido es igual?
Entonces, sí, entiendo cómo los programadores se confunden con esto. Lo he hecho yo mismo, quiero decir, escribir myString == "foo" cuando quise decir si myString.equals ("foo"). Pero a menos de rediseñar el significado del operador == para todos los objetos, no veo cómo abordarlo.
fuente
==
para significar "cadenas iguales".==
, tal como mencionó al final.==
era propenso a errores.Esta es una pregunta válida para
Strings
, y no sólo para las cadenas, sino también para otros objetos inmutables que constituyen aproximadamente el "valor", por ejemploDouble
,BigInteger
e inclusoInetAddress
.Para hacer que el
==
operador sea utilizable con cadenas y otras clases de valor, veo tres alternativas:Haga que el compilador conozca todas estas clases de valores y la forma de comparar sus contenidos. Si solo fuera un puñado de clases del
java.lang
paquete, lo consideraría, pero eso no cubre casos como InetAddress.Permita la sobrecarga del operador para que una clase defina su
==
comportamiento de comparación.Elimine los constructores públicos y tenga métodos estáticos que devuelvan instancias de un grupo, siempre devolviendo la misma instancia para el mismo valor. Para evitar pérdidas de memoria, necesita algo como SoftReferences en el grupo, que no existía en Java 1.0. Y ahora, para mantener la compatibilidad, los
String()
constructores ya no se pueden eliminar.Lo único que aún podría hacerse hoy sería introducir la sobrecarga del operador, y personalmente no me gustaría que Java vaya por esa ruta.
Para mí, la legibilidad del código es lo más importante, y un programador de Java sabe que los operadores tienen un significado fijo, definido en la especificación del lenguaje, mientras que los métodos están definidos por algún código, y su significado debe buscarse en el Javadoc del método. Me gustaría mantener esa distinción incluso si eso significa que las comparaciones de cadenas no podrán usar el
==
operador.Solo hay un aspecto de las comparaciones de Java que me molesta: el efecto de auto-boxing y -unboxing. Oculta la distinción entre el tipo primitivo y el envoltorio. Pero cuando los comparas
==
, son MUY diferentes.fuente