Todo programador Java competente sabe que você precisa usar String.equals () para comparar uma sequência, em vez de == porque == verifica a igualdade de referência.
Quando estou lidando com seqüências de caracteres, na maioria das vezes estou verificando a igualdade de valor em vez de referenciar a igualdade. Parece-me que seria mais intuitivo se o idioma permitisse a comparação dos valores de string usando apenas ==.
Como comparação, o operador de C # == verifica a igualdade de valor da string s. E se você realmente precisou verificar a igualdade de referência, pode usar String.ReferenceEquals.
Outro ponto importante é que as Strings são imutáveis, portanto, não há danos a serem causados ao permitir esse recurso.
Existe alguma razão específica para isso não ser implementado em Java?
fonte
==
é a igualdade de objetos eeq
é a igualdade de referência ( ofps.oreilly.com/titles/9780596155957/… ).==
operador mapeia apenasEquals
se o==
operador foi implementado dessa maneira. O comportamento padrão para==
é o mesmo queReferenceEquals
(na verdade,ReferenceEquals
é definida como a versão de objeto de==
)Respostas:
Eu acho que é apenas consistência, ou "princípio de menor espanto". String é um objeto, portanto, seria surpreendente se ele fosse tratado de maneira diferente de outros objetos.
Na época em que o Java foi lançado (~ 1995), apenas ter algo parecido
String
era um luxo total para a maioria dos programadores que estavam acostumados a representar strings como matrizes com terminação nula.String
O comportamento de agora é o que era naquela época, e isso é bom; alterar sutilmente o comportamento posteriormente pode ter efeitos surpreendentes e indesejados nos programas de trabalho.Como uma observação lateral, você pode usar
String.intern()
para obter uma representação canônica (interna) da sequência, após a qual as comparações podem ser feitas==
. A internação leva algum tempo, mas depois disso, as comparações serão muito rápidas.Além disso: ao contrário de algumas respostas sugeridas, não se trata de oferecer suporte à sobrecarga do operador . O
+
operador (concatenação) funciona emString
s, mesmo que o Java não suporte a sobrecarga do operador; é simplesmente tratado como um caso especial no compilador, resolvendo paraStringBuilder.append()
. Da mesma forma,==
poderia ter sido tratado como um caso especial.Então, por que surpreender com um caso especial,
+
mas não com==
? Porque+
simplesmente não é compilado quando aplicado aString
objetos que não são objetos, o que é rapidamente aparente. O comportamento diferente de==
seria muito menos aparente e, portanto, muito mais surpreendente quando atingir você.fonte
James Gosling , o criador do Java, explicou isso em julho de 2000 :
fonte
==
operador está sobrecarregado para objetos e primitivas. O+
operador é sobrecarregado parabyte
,short
,int
,long
,float
,double
,String
e, provavelmente, um par de outros que eu esqueci. Teria sido perfeitamente possível sobrecarga==
paraString
bem.Consistência no idioma. Ter um operador que age de maneira diferente pode ser surpreendente para o programador. Java não permite que os usuários sobrecarregem os operadores - portanto, igualdade de referência é o único significado razoável
==
entre objetos.Dentro do Java:
==
compara igualdade numérica==
compara igualdade booleana==
compara a identidade de referência.equals(Object o)
para comparar valoresÉ isso aí. Regra simples e simples para identificar o que você deseja. Tudo isso é coberto na seção 15.21 do JLS . Compreende três subseções fáceis de entender, implementar e raciocinar.
Depois de permitir a sobrecarga
==
, o comportamento exato não é algo que você pode procurar no JLS, colocar um dedo em um item específico e dizer "é assim que funciona", o código pode se tornar difícil de raciocinar. O comportamento exato de==
pode ser surpreendente para um usuário. Toda vez que você o vê, precisa voltar e verificar o que realmente significa.Como o Java não permite sobrecarregar os operadores, é necessário ter um teste de igualdade de valor do qual você possa substituir a definição básica. Assim, foi determinado por essas opções de design.
==
em Java testa numérico para tipos numéricos, igualdade booleana para tipos booleanos e igualdade de referência para todo o resto (que pode substituir.equals(Object o)
para fazer o que quiserem para igualdade de valor).Não se trata de "existe um caso de uso para uma conseqüência específica dessa decisão de design", mas "é uma decisão de design para facilitar essas outras coisas, é uma consequência disso".
String interning , é um exemplo disso. De acordo com o JLS 3.10.5 , todos os literais de cadeia de caracteres são internados. Outras strings são internadas se alguém as invocar
.intern()
. Isso"foo" == "foo"
é uma consequência das decisões de design tomadas para minimizar o espaço ocupado pela memória ocupado pelos literais String. Além disso, a internação de strings é algo que está no nível da JVM que tem um pouco de exposição ao usuário, mas na grande maioria dos casos, não deve ser algo que preocupa o programador (e os casos de uso para programadores não eram algo que estava no topo da lista para os designers ao considerar esse recurso).As pessoas apontam isso
+
e+=
estão sobrecarregadas com o String. No entanto, isso não é aqui nem ali. Continua sendo o caso que, se==
tiver um significado de igualdade de valor para String (e apenas String), seria necessário um método diferente (que só existe em String) para a igualdade de referência. Além disso, isso desnecessariamente complicaria os métodos que levam o Object e esperam==
se comportar de uma maneira e.equals()
se comportar de outra, exigindo que os usuários criem casos especiais de todos esses métodos para String.O contrato consistente para
==
on Objects é que ele é apenas igualdade de referência e.equals(Object o)
existe para todos os objetos que devem testar a igualdade de valor. Complicar isso complica muitas coisas.fonte
new String("foo") == new String("foo")
pode ser verdade (consulte Desduplicação de String ).Java não suporta sobrecarga de operador, o que significa que
==
se aplica apenas a tipos ou referências primitivas. Qualquer outra coisa requer a invocação de um método. Por que os designers fizeram isso é uma pergunta que apenas 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 configurá-la de tal maneira que todo primitivo é um
struct
e todostruct
é um objeto. Como o C # permite a sobrecarga do operador, esse arranjo facilita muito a qualquer classe, e não apenasString
, a trabalhar da maneira "esperada" com qualquer operador. C ++ permite a mesma coisa.fonte
==
quisesse igualdade de string, precisaríamos de outra notação para igualdade de referência.==
. Isso adiciona efetivamente a sobrecarga do operador, o que teria implicações tremendas na maneira como o Java é implementado.ClassName.ReferenceEquals(a,b)
) e um==
operador eEquals
método padrão apontados para ambosReferenceEquals
.Isso foi diferenciado em outros idiomas.
No Object Pascal (Delphi / Free Pascal) e 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 realmente amo em Pascal, obter NullreferenceException apenas por causa de uma string não inicializada é simplesmente irritante) e ter semântica de copiar na gravação, tornando (na maioria das vezes) operações de string muito barato (em outras palavras, só é perceptível quando você começa a concatenar cadeias de vários megabytes).
Portanto, é uma decisão de design de linguagem para Java. Quando eles projetaram a linguagem, seguiram o caminho C ++ (como Std :: String), de modo que as strings são objetos, o que é um truque para compensar C sem um tipo de string real, em vez de torná-las primitivas (como são).
Portanto, por um motivo, posso apenas especular que eles facilitaram o processo e não codificaram o operador, fazendo uma exceção no compilador para seqüências de caracteres.
fonte
String
deveria ter sido um tipo primitivo em Java. Diferente de outros tipos, o compilador precisa conhecerString
; além disso, as operações nele serão suficientemente comuns para que, para muitos tipos de aplicativos, elas possam representar um gargalo de desempenho (que poderia ser facilitado pelo suporte nativo). Um típicostring
[minúsculo] teria um objeto alocado no heap para manter seu conteúdo, mas nenhuma referência "normal" a esse objeto existiria em qualquer lugar; poderia, portanto, ser um indireto único,Char[]
ouByte[]
melhor, do que ter que serChar[]
indireto através de outro objeto.Em Java, não há sobrecarga de operador, e é por isso que os operadores de comparação são sobrecarregados apenas para os tipos primitivos.
A classe 'String' não é uma 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 ou 8 o oracle fez uma exceção no compilador para reconhecer
str1 == str2
comostr1.equals(str2)
fonte
s1
es2
e dar-lhes o mesmo conteúdo, eles passam a igualdade (s1.equals(s2)
comparação), mas não a mesma referência (==
comparação), porque eles são dois objetos diferentes. Alterar a semântica de==
para significar igualdade causarias1 == s2
avaliartrue
onde costumava avaliarfalse
.O Java parece ter sido projetado para manter uma regra fundamental de que o
==
operador deve ser legal a qualquer momento em que um operando possa ser convertido no tipo do outro e deve comparar o resultado dessa conversão com o operando não convertido.Esta regra dificilmente é exclusiva do Java, mas tem alguns efeitos de longo alcance (e lamentável a IMHO) no design de outros aspectos da linguagem relacionados ao tipo. Teria sido mais claro especificar os comportamentos
==
em relação a combinações particulares de tipos de operandos e proibir combinações dos tipos X e Y ondex1==y1
ex2==y1
não implicariamx1==x2
, mas as línguas raramente fazem isso [sob essa filosofia,double1 == long1
teriam que indicar sedouble1
não é uma representação exata delong1
, ou se recusa a compilar;int1==Integer1
deve ser proibido, mas deve haver um meio conveniente e eficiente de não arremessar se um objeto é um número inteiro em caixa com valor específico (a comparação com algo que não é um número inteiro em caixa deve simplesmente retornarfalse
)].Com relação à aplicação do
==
operador às seqüências de caracteres, se o Java proibisse comparações diretas entre operandos do tipoString
eObject
, poderia ter evitado surpresas no comportamento de==
, mas não há comportamento que possa ser implementado para essas comparações que não seriam surpreendentes. Ter duas referências de string mantidas no tipoObject
se comporta de maneira diferente das referências mantidas no tipoString
teria sido muito menos surpreendente do que ter um desses comportamentos diferentes do de uma comparação legal de tipo misto. SeString1==Object1
for legal, isso implicaria que o único caminho para os comportamentosString1==String2
eObject1==Object2
para corresponderString1==Object1
seria que eles se correspondessem.fonte
==
em objetos deve simplesmente chamar igual a (nulo-seguro) igual e outra coisa (por exemplo,===
ouSystem.identityEqual
) deve ser usada para a comparação de identidade. A mistura de primitivos e objetos seria inicialmente proibida (não havia autobox antes da 1.5) e, em seguida, alguma regra simples poderia ser encontrada (por exemplo, unbox com segurança nula, depois converter e comparar).int==Integer
operador retornarfalse
seInteger
nulo e, caso contrário, comparar valores, essa abordagem teria sido diferente do comportamento de==
todas as outras circunstâncias, em que coagiu incondicionalmente ambos os operandos ao mesmo tipo antes comparando-os. Pessoalmente, eu me pergunto se auto-unboxing foi posto em prática em um esforço para permitir queint==Integer
para ter um comportamento que não era absurda ...int
e fazer uma comparação de referência teria sido bobo [mas nem sempre falhava]. Caso contrário, não vejo razão para permitir uma conversão implícita que possa falhar com um NPE.==
não tem nada a veridentityEquals
. +++ "operadores de igualdade separados para igualdade de valor e referência" - mas quais? Eu consideraria tanto primitivo==
eequals
como fazê valor comparação no sentido de queequals
olha para o valor da referência. +++ Quando isso==
significaequals
, entãoint==Integer
DEVE fazer autoboxing e comparar as referências usando valores nulos e seguros. +++ Receio que minha idéia não seja realmente minha, mas apenas o que Kotlin faz.==
nunca testou a igualdade de referência, seria sensato realizar um teste de igualdade de valores com segurança nula. O fato de que ele faz igualdade de referência de teste, no entanto, limita severamente como ele pode lidar com comparações de referência / valor mistos sem inconsistência. Observe também que Java é fixo na noção de que os operadores promovem ambos os operandos para o mesmo tipo, em vez de gerar comportamentos especiais com base nas combinações de tipos envolvidos. Por exemplo,16777217==16777216.0f
retornostrue
porque ele executa uma conversão com perdas do primeiro operando afloat
, enquanto um ...Em geral, há boas razões para querer testar se duas referências de objeto apontam para o mesmo objeto. Já tive muitas vezes que escrevi
Eu posso ou não ter uma função igual nesses casos. Se sim, a função equals 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, obviamente, duas idéias muito diferentes.
Provavelmente é verdade que, para objetos imutáveis, como Strings, isso é menos problemático. 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.
Claro que isso retorna falso, mas posso ver alguém pensando que deveria ser verdade.
Mas quando você diz que == compara alças de referência e não o conteúdo de Objetos em geral, criar 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 fazê-lo apenas para Strings?
E sobre ...
Isso é falso porque são dois objetos diferentes ou verdadeiro porque são Strings cujo conteúdo é igual?
Então, sim, eu entendo como os programadores ficam confusos com isso. Eu já fiz isso, quero dizer write if myString == "foo" quando eu quis dizer if myString.equals ("foo"). Mas, além de redesenhar o significado do operador == para todos os objetos, não vejo como resolvê-lo.
fonte
==
significar "seqüências iguais".==
, exatamente como você mencionou no final.==
era propenso a erros.Esta é uma pergunta válida para
Strings
, e não apenas para cordas, mas também para outros objetos imutáveis que representam cerca de "valor", por exemploDouble
,BigInteger
e mesmoInetAddress
.Para tornar o
==
operador utilizável com Strings e outras classes de valor, vejo três alternativas:Faça com que o compilador conheça todas essas classes de valor e a maneira de comparar seu conteúdo. Se fossem apenas algumas classes do
java.lang
pacote, eu consideraria isso, mas isso não cobre casos como o InetAddress.Permitir sobrecarga do operador 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 pelo 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, 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 pesquisado no Javadoc do método. Eu gostaria de permanecer 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 de Java que é irritante para mim: o efeito do boxe automático e da caixa unificada. Oculta a distinção entre o tipo primitivo e o tipo de invólucro. Mas quando você os compara
==
, eles são MUITO diferentes.fonte