Por que a comparação do valor da string do operador == não o fez para Java?

50

Todo programador Java competente sabe que você precisa usar String.equals () para comparar uma string, em vez de == porque == verifica a igualdade de referência.

Quando estou lidando com strings, na maior parte do tempo, estou verificando a igualdade de valor em vez da igualdade de referência. Parece-me que seria mais intuitivo se a linguagem permitisse que valores de string fossem comparados usando apenas ==.

Como comparação, o operador = C # verifica a igualdade de valor para string s. E se você realmente precisava verificar a igualdade de referência, você pode usar String.ReferenceEquals.

Outro ponto importante é que as Strings são imutáveis, portanto, não há nenhum dano a ser feito ao permitir esse recurso.

Existe algum motivo específico para isso não ser implementado em Java?

    
por l46kok 02.04.2013 / 12:46
fonte

9 respostas

90

Eu acho que é apenas consistência, ou "princípio de menor espanto". String é um objeto, então seria surpreendente se fosse tratado de forma diferente de outros objetos.

No momento em que o Java apareceu (~ 1995), apenas ter algo como String era um luxo total para a maioria dos programadores que estavam acostumados a representar strings como matrizes com terminação nula. O comportamento de String é agora o que era naquela época, e isso é bom; sutilmente alterar o comportamento mais tarde poderia ter efeitos indesejáveis e surpreendentes em programas de trabalho.

Como observação, você pode usar String.intern() para obter uma representação canônica (internada) da string, após a qual as comparações podem ser feitas com == . Internar leva algum tempo, mas depois disso, as comparações serão muito rápidas.

Adição: diferentemente de algumas respostas sugeridas, não se trata de dar suporte à sobrecarga do operador . O operador + (concatenação) funciona em String s, embora o Java não suporte a sobrecarga do operador; é simplesmente tratado como um caso especial no compilador, resolvendo para StringBuilder.append() . Da mesma forma, == poderia ter sido tratado como um caso especial.

Então, por que surpreender com o caso especial + , mas não com == ? Porque, + simplesmente não compila quando aplicado a objetos que não sejam String , o que é rapidamente aparente. O diferente comportamento de == seria muito menos aparente e, portanto, muito mais surpreendente quando ele atinge você.

    
por 02.04.2013 / 12:53
fonte
32

James Gosling , o criador de Java, explicou desta forma back em julho de 2000 :

I left out operator overloading as a fairly personal choice because I had seen too many people abuse it in C++. I've spent a lot of time in the past five to six years surveying people about operator overloading and it's really fascinating, because you get the community broken into three pieces: Probably about 20 to 30 percent of the population think of operator overloading as the spawn of the devil; somebody has done something with operator overloading that has just really ticked them off, because they've used like + for list insertion and it makes life really, really confusing. A lot of that problem stems from the fact that there are only about half a dozen operators you can sensibly overload, and yet there are thousands or millions of operators that people would like to define -- so you have to pick, and often the choices conflict with your sense of intuition.

    
por 02.04.2013 / 13:24
fonte
9

Consistência no idioma. Ter um operador que aja de maneira diferente pode ser surpreendente para o programador. Java não permite que os usuários sobrecarregem os operadores - portanto, a igualdade de referência é o único significado razoável para == entre objetos.

Dentro de Java:

  • Entre tipos numéricos, == compara a igualdade numérica
  • Entre tipos booleanos, == compara a igualdade booleana
  • Entre objetos, == compara a identidade de referência
    • Use .equals(Object o) para comparar valores

É isso. Regra simples e simples para identificar o que você quer. Tudo isso é coberto na seção 15.21 do JLS . É composto por três subseções que são fáceis de entender, implementar e raciocinar.

Depois de permitir a sobrecarga de == , o Comportamento exato não é algo que você pode olhar para o JLS e colocar o dedo em um item específico e dizer "é assim que funciona", o código pode tornar-se difícil de raciocinar. O comportamento exato de == pode ser surpreendente para um usuário. Toda vez que você vê, você tem que voltar e checar para ver o que realmente significa.

Como o Java não permite a sobrecarga de operadores, é necessário ter um teste de igualdade de valor que você possa sobrescrever a definição base. Assim, foi mandatado por essas escolhas de design. == em testes Java numéricos para tipos numéricos, igualdade booleana para tipos booleanos e igualdade de referência para todo o resto (o que pode substituir .equals(Object o) para fazer o que quiser para igualdade de valor).

Esta não é uma questão de "existe um caso de uso para uma conseqüência específica desta decisão de design", mas sim "esta é uma decisão de design para facilitar essas outras coisas, isso é uma conseqüência disso."

Cadeia de internação , é um exemplo disso. De acordo com o JLS 3.10.5 , Todos os literais de string são internados. Outras strings são internadas se alguém invocar .intern() nelas. Que "foo" == "foo" é true é uma consequência das decisões de design feitas para minimizar a pegada de memória ocupada pelos literais String. Além disso, String interning é algo que está no nível da JVM que tem um pouco de exposição para o usuário, mas na grande maioria esmagadora dos casos, não deve ser algo que diz respeito ao programador (e casos de uso para programadores não eram algo que estava no topo da lista para os designers ao considerar esse recurso).

As pessoas apontarão que + e += estão sobrecarregados para String. No entanto, isso não é nem aqui nem ali. Permanece o caso que se == tem um significado de igualdade de valor para String (e somente String), seria necessário um método diferente (que existe apenas em String) para igualdade de referência. Além disso, isso complicaria desnecessariamente os métodos que tomam Object e esperam que o == se comporte de uma maneira e .equals() se comportem de outra maneira, exigindo que os usuários enviem todos os métodos aqueles para o String.

O contrato consistente para == on Objects é que é somente igualdade de referência e que .equals(Object o) existe para todos os objetos que devem testar para igualdade de valor. Complicar isso complica muitas coisas.

    
por 19.10.2015 / 22:34
fonte
2

O Java não suporta sobrecarga do operador, o que significa que == se aplica apenas a tipos primitivos ou referências. Qualquer outra coisa requer a invocação de um método. Por que os designers fizeram isso é uma pergunta que só eles podem responder. Se eu tivesse que adivinhar, provavelmente é porque a sobrecarga do operador traz complexidade que eles não estavam interessados em adicionar.

Eu não sou especialista em C #, mas os designers dessa linguagem parecem ter configurado de tal forma que cada primitivo é um struct e cada struct é um objeto. Como o C # permite a sobrecarga do operador, esse arranjo torna muito fácil para qualquer classe, não apenas String , fazer com que ela funcione da maneira "esperada" com qualquer operador. C ++ permite a mesma coisa.

    
por 02.04.2013 / 12:59
fonte
2

Isso foi diferente em outros idiomas.

No Object Pascal (Delphi / Free Pascal) e no C #, o operador de igualdade é definido para comparar valores, não referências, ao operar em strings.

Particularmente em Pascal, string é um tipo primitivo (uma das coisas que eu mais amo em Pascal, obter NullreferenceException apenas por causa de uma string não inicializada é simplesmente irritante) e ter semântica copy-on-write fazendo assim (a maior parte do tempo) operações de strings muito baratas (em outras palavras, somente perceptível uma vez que você comece a concatenar strings multi-megabytes).

Então, é uma decisão de design de linguagem para Java. Quando eles projetaram a linguagem, eles seguiram o caminho C ++ (como Std :: String), então strings são objetos, que é um truque para compensar C, faltando um tipo de string real, em vez de fazer strings um primitivo (o que eles são). / p>

Então, por uma razão, eu só posso especular que eles fizeram isso para facilitar seu lado e não codificar o operador fazer uma exceção no compilador para seqüências de caracteres.

    
por 02.04.2013 / 23:08
fonte
1

Em Java, não há sobrecarga de operador, e é por isso que os operadores de comparação só são sobrecarregados para os tipos primitivos.

A classe 'String' não é primitiva, portanto não possui uma sobrecarga para '==' e usa o padrão de comparar o endereço do objeto na memória do computador.

Não tenho certeza, mas acho que no Java 7 or 8 o Oracle fez uma exceção no compilador para reconhecer str1 == str2 as str1.equals(str2)

    
por 02.04.2013 / 12:53
fonte
0

O Java parece ter sido projetado para manter uma regra fundamental de que o operador == deve ser legal sempre que um operando puder ser convertido para o tipo do outro e comparar o resultado dessa conversão com o não convertido. operando.

Esta regra dificilmente é exclusiva do Java, mas tem alguns efeitos de longo alcance (e IMHO) no design de outros aspectos relacionados ao tipo da linguagem. Teria sido mais limpo especificar os comportamentos de == em relação a combinações particulares de tipos de operandos e proibir combinações de tipos X e Y em que x1==y1 e x2==y1 não implicariam x1==x2 , mas as linguagens raramente fazem que [sob essa filosofia, double1 == long1 teria que indicar se double1 não é uma representação exata de long1 , ou se recusaria a compilar; int1==Integer1 deve ser proibido, mas deve haver um meio conveniente e eficiente de testar se um objeto é um inteiro com valor específico (comparação com algo que não é um inteiro na caixa deve simplesmente retornar false )] .

Com relação à aplicação do operador == às cadeias de caracteres, se o Java proibiu comparações diretas entre operandos do tipo String e Object , isso poderia ter evitado surpresas no comportamento de == , mas há nenhum comportamento poderia implementar para tais comparações que não seriam surpreendentes. Ter duas referências de string mantidas no tipo Object se comportarem de maneira diferente das referências mantidas no tipo String teria sido muito menos surpreendente do que ter um desses comportamentos diferentes do de uma comparação legal de tipo misto. Se String1==Object1 for legal, isso implicaria que a única maneira de os comportamentos de String1==String2 e Object1==Object2 corresponderem a String1==Object1 seria que eles correspondam entre si.

    
por 24.12.2013 / 20:24
fonte
0

Em geral, há boas razões para querer testar se duas referências de objeto apontam para o mesmo objeto. Eu tive muitas vezes que eu escrevi

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

Eu posso ou não ter uma função igual em tais casos. Se eu fizer isso, a função igual pode comparar todo o conteúdo de ambos os objetos. Frequentemente, apenas compara algum identificador. "A e B são referências ao mesmo objeto" e "A e B são dois objetos diferentes com o mesmo conteúdo" são, naturalmente, duas idéias muito diferentes.

É provavelmente verdade que, para objetos imutáveis, como o Strings, isso é um problema menor. Com objetos imutáveis, tendemos a pensar no objeto e no valor como sendo a mesma coisa. Bem, quando digo "nós", quero dizer "eu", pelo menos.

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

É claro que isso retorna falso, mas vejo alguém achando que deveria ser verdade.

Mas uma vez que você diz que == compara alças de referência e não conteúdo para Objetos em geral, fazer um caso especial para Strings seria potencialmente confuso. Como alguém aqui disse, e se você quisesse comparar as alças de dois objetos String? Haveria alguma função especial para fazer isso apenas para Strings?

E sobre ...

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

Isso é falso porque são dois objetos diferentes ou verdadeiros porque são Strings cujos conteúdos são iguais?

Então, sim, eu entendo como os programadores ficam confusos com isso. Eu fiz isso sozinho, quero dizer escrever se myString == "foo" quando eu quis dizer se myString.equals ("foo"). Mas, além de redesenhar o significado do operador == para todos os objetos, não vejo como abordá-lo.

    
por 14.03.2016 / 14:49
fonte
0

Esta é uma pergunta válida para Strings , e não apenas para strings, mas também para outros Objetos imutáveis que representam algum "valor", por exemplo, Double , BigInteger e até InetAddress .

Para tornar o operador == utilizável com Strings e outras classes de valor, vejo três alternativas:

  • O compilador conhece todas essas classes de valor e a maneira de comparar seu conteúdo. Se fosse apenas um punhado de classes do pacote java.lang , eu consideraria isso, mas isso não cobre casos como InetAddress.

  • Permitir sobrecarga de operadores para que uma classe defina seu comportamento de comparação == .

  • Remova os construtores públicos e tenha métodos estáticos retornando instâncias de um pool, sempre retornando a mesma instância para o mesmo valor. Para evitar vazamentos de memória, você precisa de algo como SoftReferences no pool, que não existia no Java 1.0. E agora, para manter a compatibilidade, os String() construtores não podem mais ser removidos.

A única coisa que ainda poderia ser feita hoje seria introduzir a sobrecarga do operador, e pessoalmente eu não gostaria que o Java seguisse esse caminho.

Para mim, a legibilidade do código é mais importante, e um programador Java sabe que os operadores têm um significado fixo, definido na especificação da linguagem, enquanto os métodos são definidos por algum código e seu significado deve ser procurado no método. Javadoc. Eu gostaria de ficar com essa distinção, mesmo que isso signifique que as comparações de String não poderão usar o operador == .

Há apenas um aspecto das comparações do Java que me incomoda: o efeito de auto-boxing e -unboxing. Esconde a distinção entre o primitivo e o tipo de invólucro. Mas quando você os compara com == , eles são MUITO 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?
    
por 17.08.2017 / 13:21
fonte