Sobrescritura de hashCode y ==
Parte de la sección Programación Orientada a Objetos del Journey de Dart de Coddy — lección 71 de 110.
Por defecto, dos objetos en Dart solo son iguales si son exactamente la misma instancia en memoria. Esto significa que dos objetos con valores idénticos se siguen considerando diferentes:
class Point {
int x, y;
Point(this.x, this.y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - ¡instancias diferentes!
}Para comparar objetos por sus valores, necesitas sobrescribir tanto el operador == como hashCode. Estos dos siempre deben sobrescribirse juntos: si dos objetos son iguales, deben tener el mismo código hash:
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}
void main() {
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // true - ¡mismos valores!
}El hashCode es crucial para colecciones como las claves de Set y Map. El ayudante Object.hash() de Dart combina múltiples valores en un único código hash. Sin un hashCode adecuado, tus objetos no funcionarán correctamente en colecciones basadas en hash incluso si se sobrescribe ==.
Desafío
Fácil¡Vamos a construir un sistema de coordenadas donde los puntos se puedan comparar por sus valores en lugar de sus direcciones de memoria! Crearás una clase Coordinate que implemente correctamente la igualdad basada en valores, haciendo que funcione correctamente con las comprobaciones de igualdad y las colecciones basadas en hash como Set.
Organizarás tu código en dos archivos:
coordinate.dart: Crea una claseCoordinateque represente un punto en el espacio 2D con los camposfinal int xyfinal int y. Sobrescribe tanto el operador==como elhashCodepara que dos coordenadas con los mismos valores de x e y se consideren iguales. UtilizaObject.hash()para generar el código hash a partir de ambos campos.main.dart: Importa tu archivo de coordenadas y demuestra cómo la igualdad basada en valores cambia el comportamiento de comparación de objetos:- Crea dos objetos
Coordinateseparados, ambos con x:5e y:10 - Imprime si son iguales usando
== - Imprime si son idénticos (la misma instancia) usando
identical() - Crea un
Set<Coordinate>y añade ambas coordenadas a él - Imprime la longitud del conjunto (¡debería ser 1 ya que son iguales!)
- Crea una tercera coordenada con x:
3e y:7, añádela al conjunto - Imprime la longitud final del conjunto
- Crea dos objetos
Este desafío demuestra por qué hashCode y == siempre deben sobrescribirse juntos. El Set utiliza códigos hash para organizar los elementos, por lo que sin un hashCode adecuado, ¡incluso los objetos iguales podrían almacenarse como duplicados!
Salida esperada:
true
false
1
2Hoja de referencia
Por defecto, dos objetos en Dart son iguales solo si son la misma instancia en memoria, no si tienen los mismos valores:
var p1 = Point(3, 4);
var p2 = Point(3, 4);
print(p1 == p2); // false - ¡instancias diferentes!Para comparar objetos por sus valores, sobrescribe tanto el operador == como hashCode. Estos siempre deben sobrescribirse juntos:
class Point {
final int x, y;
Point(this.x, this.y);
@override
bool operator ==(Object other) {
if (other is! Point) return false;
return x == other.x && y == other.y;
}
@override
int get hashCode => Object.hash(x, y);
}Usa Object.hash() para combinar múltiples valores en un solo código hash. El hashCode es crucial para colecciones como Set y Map; sin él, los objetos iguales no funcionarán correctamente en colecciones basadas en hash.
Para comprobar si dos objetos son la misma instancia (no solo valores iguales), usa identical():
print(identical(p1, p2)); // false - instancias diferentesPruébalo tú mismo
import 'coordinate.dart';
void main() {
// TODO: Crea dos objetos Coordinate separados, ambos con x: 5 e y: 10
// TODO: Imprime si son iguales usando ==
// TODO: Imprime si son idénticos (la misma instancia) usando identical()
// TODO: Crea un Set<Coordinate> y añade ambas coordenadas a él
// TODO: Imprime la longitud del conjunto (¡debería ser 1 ya que son iguales!)
// TODO: Crea una tercera coordenada con x: 3 e y: 7, y añádela al conjunto
// TODO: Imprime la longitud final del conjunto
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO
Archivos externosLibrerías e importacionesIntroducción a la POOClases vs. ObjetosLa palabra clave thisMétodosVariables de instanciaConceptos básicos de constructoresResumen - Calculadora sencilla4Seguridad de nulos
Introducción a la seguridad de nulosNulables vs No nulablesLos operadores ? y !Palabra clave Late y Null SafetyOperadores Null-awareNull Safety en clasesResumen - Sistema de perfil de usuario7Clases abstractas e interfaces
Clases abstractasMétodos abstractosInterfaces en DartInterfaces implícitasImplementar vs. ExtenderInterfaces múltiplesRepaso - Calculadora de figuras10Colecciones y Genéricos
Visión general de List, Set y MapColecciones con seguridad de tiposClases genéricasMétodos genéricosRestricciones genéricasIterable e IteratorResumen - Almacenamiento genérico2Constructores en Dart
Constructor por defectoConstructores nombradosListas de inicializaciónConstructores constantesConstructores de fábricaConstructores de redireccionamientoResumen - Constructor de formas5Encapsulamiento
Miembros públicos vs. privadosLa convención del prefijo _Privacidad a nivel de libreríaProfundidad en Getters y SettersOcultamiento de informaciónResumen - Registros de estudiantes8Mixins
Introducción a los MixinsCreación de MixinsUso de múltiples MixinsPalabra clave on en MixinsMixin vs HerenciaMixin vs InterfazResumen - Sistema Animal11Métodos especiales
Sobrescritura de toString()Sobrescritura de hashCode y ==Interfaz ComparableMétodo call()Sobrescritura de noSuchMethodResumen - Colección personalizada14Patrones de diseño Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón ObserverPatrón Strategy3Propiedades de clase
Miembros de instancia vs. estáticosCampos Final y ConstVariables LateMétodos y campos estáticosGetters y SettersResumen - Gestor de cuentas bancarias6Herencia
Herencia básicaLa palabra clave superSobrescritura de métodosLa anotación @overrideLa palabra clave final en clasesConstructores y herenciaResumen - Jerarquía de empleados9Polimorfismo
Fundamentos del polimorfismoPolimorfismo mediante interfacesComprobación de tipos en tiempo de ejecuciónLos operadores is y asPalabra clave covariantResumen - Procesador de pagos12POO Asíncrona
Futures y async/awaitFundamentos de StreamsStream ControllersConstructores asíncronosAsincronía en métodos de claseRepaso - Data Fetcher15Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Repository