¿Por qué no == la comparación del valor de la cadena del operador llegó a Java?

50

Todo programador Java competente sabe que necesitas usar String.equals () para comparar una cadena, en lugar de == porque == comprueba la igualdad de referencia.

Cuando trato con cadenas, la mayoría de las veces estoy comprobando la igualdad de valores en lugar de la igualdad de referencia. Me parece que sería más intuitivo si el idioma permitiera que los valores de cadena se compararan con solo ==.

Como comparación, C # 's == operador comprueba la igualdad de valores para la cadena s. Y si realmente necesitaba verificar la igualdad de referencia, puede usar String.ReferenceEquals.

Otro punto importante es que las cadenas son inmutables, por lo que no se puede hacer daño al permitir esta función.

¿Hay alguna razón en particular por la que esto no esté implementado en Java?

    
pregunta l46kok 02.04.2013 - 12:46

9 respuestas

90

Supongo que es solo consistencia, o "principio de menos asombro". La cadena es un objeto, por lo que sería sorprendente que se tratara de forma diferente a otros objetos.

En el momento en que salió Java (~ 1995), simplemente tener algo como String era un lujo total para la mayoría de los programadores que estaban acostumbrados a representar cadenas como arreglos terminados en cero. El comportamiento de String es ahora 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 (internada) de la cadena, después de lo cual se podrían hacer comparaciones con == . La pasantía lleva algún tiempo, pero después de eso, las comparaciones serán realmente rápidas.

Adición: a diferencia de lo que sugieren algunas respuestas, no se trata de soportar la sobrecarga del operador . El operador + (concatenación) funciona en String s a pesar de que Java no admite la sobrecarga de operadores; simplemente se maneja como un caso especial en el compilador, resolviendo a StringBuilder.append() . De manera similar, == podría haberse manejado como un caso especial.

Entonces, ¿por qué sorprender con el caso especial + pero no con == ? Debido a que, + simplemente no compila cuando se aplica a objetos que no son String , por lo que es evidente. El diferente comportamiento de == sería mucho menos aparente y, por lo tanto, mucho más sorprendente cuando te golpea.

    
respondido por el Joonas Pulakka 02.04.2013 - 12:53
32

James Gosling , el creador de Java, lo explicó de esta manera en julio de 2000 :

  

He omitido la sobrecarga del operador como algo bastante personal   elección porque había visto demasiada gente abusar de ella en C ++. he gastado   una gran cantidad de tiempo en los últimos cinco a seis años encuestando a personas sobre   sobrecarga de operadores y es realmente fascinante, porque obtienes la   comunidad dividida en tres partes: Probablemente alrededor del 20 al 30 por ciento de   la población piensa en la sobrecarga del operador como el engendro de la   diablo; alguien ha hecho algo con la sobrecarga del operador que tiene   realmente los marcó, porque han usado como + para la lista   La inserción y hace la vida realmente, muy confusa. Mucho de eso   El problema surge del hecho de que solo hay alrededor de media docena   operadores que pueden sobrecargarse sensiblemente, y sin embargo hay miles o   millones de operadores que la gente quisiera definir, por lo que tiene   Para elegir, y con frecuencia las elecciones entran en conflicto con su sentido de la intuición.

    
respondido por el Ross Patterson 02.04.2013 - 13:24
9

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:

  • Entre los tipos numéricos, == compara la igualdad numérica
  • Entre los tipos booleanos, == compara la igualdad booleana
  • Entre los objetos, == compara la identidad de referencia
    • Use .equals(Object o) para comparar valores

Eso es todo. Regla simple y sencilla para identificar lo que quieres. Todo esto está cubierto en sección 15.21 del JLS . Comprende tres subsecciones que son fáciles de entender, implementar y razonar.

Una vez que permita la sobrecarga de == , el comportamiento exacto no es algo que pueda ver en el JLS y poner el 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 veas, tienes que regresar y verificar lo que realmente significa.

Dado que Java no permite la sobrecarga de operadores, se necesita una forma de tener una prueba de igualdad de valores en la que se pueda anular la definición básica de. Por lo tanto, fue mandado por estas opciones de diseño. == en Java prueba los tipos numéricos, la igualdad booleana para los tipos booleanos y la igualdad de referencia para todo lo demás (que puede reemplazar a .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 que "esta es una decisión de diseño para facilitar estas otras cosas, esto es una consecuencia de ello".

Interning de cadena , es un ejemplo de esto. De acuerdo con JLS 3.10.5 , Todos los literales de cadena están internados. Otras cadenas se internan si se invoca .intern() en ellas. Que "foo" == "foo" sea 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, String interning es algo que está en el nivel de JVM que tiene un poco de exposición para el usuario, pero en la inmensa mayoría de los casos, no debe ser algo que concierne al programador (y los casos de uso para programadores no lo son). algo que estaba en la lista de los diseñadores al considerar esta característica).

La gente indicará que + y += están sobrecargados para String. Sin embargo, eso no es ni aquí ni allá. Sigue siendo el caso que si == tiene un significado de igualdad de valor para String (y solo String), se 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 a Object y esperan que == se comporte de una manera y .equals() se comporte a otros usuarios que requieren el caso especial de todos los métodos esos para String.

El contrato consistente para == en Objetos es que solo es igualdad de referencia y que .equals(Object o) existe para todos los objetos que debe probar para la igualdad de valor. Complicar esto complica demasiadas cosas.

    
respondido por el user40980 19.10.2015 - 22:34
2

Java no admite la sobrecarga de operadores, 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 se deba a que la sobrecarga del operador trae una complejidad que no estaban interesados en agregar.

No soy un experto en C #, pero los diseñadores de ese lenguaje parecen haberlo configurado de modo que cada primitiva sea un struct y cada struct sea un objeto. Debido a que C # permite la sobrecarga del operador, esa disposición hace que sea muy fácil para cualquier clase, no solo String , hacer que funcione de la manera "esperada" con cualquier operador. C ++ permite lo mismo.

    
respondido por el Blrfl 02.04.2013 - 12:59
2

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 con cadenas.

Particularmente en Pascal, la cadena es un tipo primitivo (una de las cosas que realmente amo de Pascal, obtener la NullreferenceException solo porque una cadena no inicializada es simplemente irritante) y tiene una semántica de copia en escritura por lo tanto, las operaciones de cadena (la mayoría de las veces) son muy baratas (en otras palabras, solo se notan 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), de modo que las cadenas son objetos, que, en mi humilde opinión, son un truco para compensar a C que carece 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 lo hicieron muy fácil por su parte y no codificando que el operador haga una excepción en el compilador a las cadenas.

    
respondido por el Fabricio Araujo 02.04.2013 - 23:08
1

En Java, no hay ninguna sobrecarga de operadores, y es por eso que los operadores de comparación solo están sobrecargados para los tipos primitivos.

La clase 'String' no es una primitiva, por lo tanto, no tiene una sobrecarga para '==' y usa el valor predeterminado para 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 como str1.equals(str2)

    
respondido por el Gilad Naaman 02.04.2013 - 12:53
0

Java parece haber sido diseñado para mantener una regla fundamental según la cual el operador == debería ser legal cada vez que un operando pueda convertirse al tipo del otro, y debe comparar el resultado de dicha conversión con el no convertido. operando.

Esta regla no es exclusiva de Java, pero tiene algunos efectos de largo alcance (y IMHO desafortunados) en el diseño de otros aspectos del lenguaje relacionados con el tipo. Hubiera sido más limpio especificar los comportamientos de == con respecto a combinaciones particulares de tipos de operandos, y prohibir combinaciones de los tipos X e Y donde x1==y1 y x2==y1 no implicarían x1==x2 , pero los idiomas rara vez lo hacen que [bajo esa filosofía, double1 == long1 tendría que indicar si double1 no es una representación exacta de long1 , o bien rechazar la compilación; Se debe prohibir int1==Integer1 , pero debe haber un medio conveniente y eficiente de no lanzar 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 devolver false )] .

Con respecto a la aplicación del operador == a las cadenas, si Java hubiera prohibido las comparaciones directas entre operandos de tipo String y Object , podría haber evitado sorpresas en el comportamiento de == , pero hay ningún comportamiento podría implementarse para tales comparaciones que no serían sorprendentes. Tener dos referencias de cadena mantenidas en el tipo Object se comporta de manera diferente a las referencias mantenidas en el tipo String hubiera sido mucho menos sorprendente que cualquiera de esos comportamientos difiera de la de una comparación de tipo mixto legal. Si String1==Object1 es legal, eso implicaría que la única manera de que los comportamientos de String1==String2 y Object1==Object2 coincidan con String1==Object1 sería que coincidan entre sí.

    
respondido por el supercat 24.12.2013 - 20:24
0

En general, hay una buena razón para querer probar si dos referencias de objetos apuntan al mismo objeto. He tenido muchas veces que he escrito

Address oldAddress;
Address newAddress;
... populate values ...
if (oldAddress==newAddress)
... etc ...

Puede o no tener una función igual en tales casos. Si lo hago, la función de igual puede comparar el contenido completo de ambos objetos. A menudo simplemente 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 los objetos inmutables, como las cadenas, esto es un problema menor. Con los objetos inmutables, tendemos a pensar que el objeto y el valor son lo mismo. Bueno, cuando digo "nosotros", quiero decir "yo", al menos.

Integer three=new Integer(3);
Integer triangle=new Integer(3);
if (three==triangle) ...

Por supuesto que devuelve falso, pero puedo ver a alguien que piensa que debería ser cierto.

Pero una vez que digas que == compara los manejadores de referencia y no los contenidos para los Objetos en general, hacer un caso especial para las Cadenas sería potencialmente confuso. Como dijo alguien más aquí, ¿qué pasaría si quisiera comparar los manejadores de dos objetos String? ¿Habría alguna función especial para hacerlo solo para cadenas?

Y qué pasa con ...

Object x=new String("foo");
Object y=new String("foo");
if (x==y) ...

¿Eso es falso porque son dos objetos diferentes o verdadero porque son cadenas cuyos contenidos son iguales?

Así que sí, entiendo cómo los programadores se confunden con esto. Lo hice yo mismo, me refiero a escribir si 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.

    
respondido por el Jay 14.03.2016 - 14:49
0

Esta es una pregunta válida para Strings , y no solo para cadenas, sino también para otros Objetos inmutables que representan algún "valor", por ejemplo. Double , BigInteger e incluso InetAddress .

Para hacer que el operador == sea utilizable con cadenas y otras clases de valor, veo tres alternativas:

  • Haz que el compilador conozca todas estas clases de valor y la forma de comparar sus contenidos. Si fuera solo un puñado de clases del paquete java.lang , lo consideraría, pero eso no cubre casos como InetAddress.

  • Permitir la sobrecarga del operador para que una clase defina su comportamiento de comparación == .

  • Elimine los constructores públicos y haga que los métodos estáticos devuelvan instancias de un grupo, siempre devolviendo la misma instancia por el mismo valor. Para evitar pérdidas de memoria, necesita algo como las referencias de SoftReferences en el grupo, que no existían en Java 1.0. Y ahora, para mantener la compatibilidad, los constructores String() ya no pueden eliminarse.

Lo único que todavía se podría hacer hoy sería introducir la sobrecarga del operador y, personalmente, no me gustaría que Java siguiera 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 los métodos. Javadoc. Me gustaría seguir con esa distinción incluso si eso significa que las comparaciones de cadenas no podrán usar el operador == .

Hay solo un aspecto de las comparaciones de Java que me molesta: el efecto del auto-boxeo y el -boxeo. Oculta la distinción entre el tipo primitivo y el de envoltura. Pero cuando los comparas con == , son MUY diferentes.

    int i=123456;
    Integer j=123456;
    Integer k=123456;
    System.out.println(i==j);  // true or false? Do you know without reading the specs?
    System.out.println(j==k);  // true or false? Do you know without reading the specs?
    
respondido por el Ralf Kleberhoff 17.08.2017 - 13:21

Lea otras preguntas en las etiquetas