Eu li este artigo: Como escrever um método de igualdade em Java .
Basicamente, ele fornece uma solução para um método equals () que suporta herança:
Point2D twoD = new Point2D(10, 20);
Point3D threeD = new Point3D(10, 20, 50);
twoD.equals(threeD); // true
threeD.equals(twoD); // true
Mas é uma boa idéia? essas duas instâncias parecem iguais, mas podem ter dois códigos de hash diferentes. Isso não está um pouco errado?
Acredito que isso seria melhor alcançado ao lançar os operandos.
java
c#
scala
comparison
Wes
fonte
fonte
z
coordenada zero pode ser uma convenção útil para algumas aplicações (os primeiros sistemas CAD que lidam com dados legados vêm à mente). Mas é uma convenção arbitrária. Aviões em espaços com 3 ou mais dimensões podem ter orientações arbitrárias ... é o que torna interessantes problemas interessantes.Respostas:
Isso não deve ser igualdade porque quebra a transitividade . Considere estas duas expressões:
Como a igualdade é transitiva, isso significa que a seguinte expressão também é verdadeira:
Mas é claro - não é.
Portanto, sua idéia de transmissão está correta - espere que, em Java, transmissão simplesmente signifique transmissão do tipo de referência. O que você realmente deseja aqui é um método de conversão que crie um novo
Point2D
objeto a partir de umPoint3D
objeto. Isso também tornaria a expressão mais significativa:fonte
(10, 20, 50)
igual(10, 20, 60)
é bom. Nós nos preocupamos apenas com10
e20
.Point2D
ter umprojectXYZ()
método para fornecer umaPoint3D
representação de si mesmo? Em outras palavras, as implementações devem se conhecer?Point2D
parece mais simples, já que projetar pontos 2D requer a definição de seu plano no espaço 3D primeiro. Se o ponto 2D sabe que é plano, já é um ponto 3D. Se não, não pode projetar. Lembro-me do Flatland de Abbott .Plane3D
objeto, que definirá um plano no espaço 3D, esse plano pode ter umlift
método (2D-> 3D está levantando, não projetando) que aceitará umPoint2D
e um número para o "terceiro eixo" "- distância do plano ao longo do plano normal. Para facilidade de uso, você pode definir os aviões comuns como constantes estáticas, assim que você poderia fazer coisas comoPlane3D.XY.lift(new Point2D(10, 20), 50).equals(new Point3D(10, 20, 50))
Afasto-me da leitura do artigo pensando na sabedoria de Alan J. Perlis:
O fato de acertar a "igualdade" é o tipo de problema que mantém o inventor de Scala, de Martin Ordersky, acordado à noite deve dar uma pausa sobre se a substituição
equals
em uma árvore de herança é uma boa idéia.O que acontece quando não temos sorte em obter uma
ColoredPoint
é que nossa geometria falha porque usamos a herança para proliferar tipos de dados em vez de criar uma boa. Apesar de ter que voltar e modificar o nó raiz da árvore de herança para fazer oequals
trabalho. Por que não apenas adicionar umz
e umcolor
aPoint
?O bom motivo seria isso
Point
eColoredPoint
operaria em domínios diferentes ... pelo menos se esses domínios nunca se misturassem. No entanto, se for esse o caso, não precisamos substituirequals
. A comparaçãoColoredPoint
ePoint
a igualdade só fazem sentido em um terceiro domínio onde eles podem se misturar. E, nesse caso, provavelmente é melhor ter a "igualdade" adaptada ao terceiro domínio, em vez de tentar aplicar a semântica da igualdade de um ou outro ou de ambos os domínios não combinados. Em outras palavras, "igualdade" deve ser definida como local para o lugar em que temos lama fluindo de ambos os lados, porque talvez não desejemosColoredPoint.equals(pt)
falhar contra instâncias,Point
mesmo que o autor do livro tenhaColoredPoint
pensado que era uma boa idéia seis meses atrás às 2 da manhã. .fonte
Quando os antigos deuses da programação estavam inventando a programação orientada a objetos com classes, eles decidiram, quando se tratava de composição e herança, ter dois relacionamentos para um objeto: "é um" e "tem um".
Isso resolveu parcialmente o problema das subclasses serem diferentes das classes pai, mas as tornou utilizáveis sem quebrar o código. Como uma instância de subclasse "é um" objeto de superclasse e pode ser substituída diretamente por ele, mesmo que a subclasse tenha mais funções-membro ou membros de dados, a "possui-a" garante que ele executará todas as funções do pai e terá todas as suas membros. Então, você poderia dizer que um Point3D "é um" Point e um Point2D "é um" Point se ambos herdarem do Point. Além disso, um Point3D pode ser uma subclasse de Point2D.
Entretanto, a igualdade entre as classes é específica do domínio do problema, e o exemplo acima é ambíguo quanto ao que o programador precisa para o programa funcionar corretamente. Geralmente, as regras do domínio matemático são seguidas e os valores dos dados geram igualdade se você limitar o escopo da comparação apenas neste caso a duas dimensões, mas não se você comparar todos os membros dos dados.
Então, você obtém uma tabela de igualdade de igualdade:
Geralmente, você escolhe as regras mais rígidas que ainda pode executar todas as funções necessárias no domínio do problema. Os testes de igualdade internos para números são projetados para serem o mais restritivos possível para fins de matemática, mas o programador tem muitas maneiras de contornar isso, se esse não for o objetivo, incluindo arredondamento para cima / baixo, truncamento, gt, lt, etc. . Objetos com registro de data e hora são frequentemente comparados pelo tempo de geração e, portanto, cada instância deve ser única para que as comparações sejam muito específicas.
O fator de design nesse caso é determinar maneiras eficientes de comparar objetos. Às vezes, é necessário fazer uma comparação recursiva de todos os membros dos dados dos objetos, e isso pode ficar muito caro se você tiver muitos e muitos objetos com muitos membros dos dados. As alternativas são comparar apenas valores de dados relevantes ou fazer com que o objeto gere um valor de hash de seus membros de dados envolvidos para uma comparação rápida com outros objetos semelhantes, mantenha as coleções classificadas e removidas para tornar as comparações mais rápidas e menos intensivas na CPU, e talvez permitir objetos que são idênticos nos dados a serem descartados e um ponteiro duplicado para um único objeto é colocado em seu lugar.
fonte
A regra é que, sempre que você substitui
hashcode()
, você substituiequals()
e vice-versa. Se é uma boa ideia ou não, depende do uso pretendido. Pessoalmente, eu usaria um método diferente (isLike()
ou similar) para obter o mesmo efeito.fonte
Muitas vezes, é útil que classes não públicas tenham um método de teste de equivalência que permita que objetos de tipos diferentes se considerem "iguais" se representarem a mesma informação, mas porque o Java não permite meios pelos quais as classes podem se passar por elas. outro, geralmente é bom ter um único tipo de invólucro voltado para o público nos casos em que seja possível ter objetos equivalentes com representações diferentes.
Por exemplo, considere uma classe que encapsule uma matriz 2D imutável de
double
valores. Se um método externo solicita uma matriz de identidade do tamanho 1000, um segundo solicita uma matriz diagonal e passa uma matriz contendo 1000 unidades, e um terceiro solicita uma matriz 2D e passa uma matriz 1000x1000 onde os elementos na diagonal primária são todos 1,0 e todos os outros são zero, os objetos fornecidos para as três classes podem usar diferentes repositórios internamente [o primeiro com um único campo de tamanho, o segundo com uma matriz de mil elementos e o terceiro com mil matrizes de 1000 elementos], mas devem se reportar como equivalentes [já que todos os três encapsulam uma matriz imutável de 1000x1000 com as na diagonal e zeros em qualquer outro lugar].Além do fato de ocultar a existência de tipos distintos de loja de apoio, o wrapper também será útil para facilitar comparações, pois a verificação de itens quanto à equivalência geralmente será um processo de várias etapas. Pergunte ao primeiro item se ele sabe se é igual ao segundo; se não souber, pergunte ao segundo se ele sabe se é igual ao primeiro. Se nenhum dos objetos souber, pergunte a cada matriz sobre o conteúdo de seus elementos individuais [pode-se adicionar outras verificações antes de decidir fazer a rota de comparação de itens individuais por muito tempo lenta].
Observe que o método de teste de equivalência para cada objeto nesse cenário precisaria retornar um valor de três estados ("Sim, eu sou equivalente", "Não, eu não sou equivalente" ou "Não sei"), portanto, o método "igual" normal não seria adequado. Embora qualquer objeto possa simplesmente responder "eu não sei" quando perguntado sobre qualquer outro, adicionar lógica a uma matriz diagonal, por exemplo, que não incomodaria perguntar a qualquer matriz de identidade ou matriz diagonal sobre quaisquer elementos fora da diagonal principal, agilizaria bastante as comparações entre tais elementos. tipos.
fonte