Man bieži patīk izmantot šo emuāru, lai atkārtoti apmeklētu grūti nopelnītās nodarbības Java pamatos. Šis emuāra ziņojums ir viens no šādiem piemēriem un koncentrējas uz bīstamo spēku ilustrāciju aiz metodēm vienāds (Object) un hashCode (). Es neaptvēršu visas šo divu ļoti nozīmīgo metožu nianses, kas visiem Java objektiem ir skaidri deklarēti vai netieši mantoti no vecākiem (iespējams, tieši no paša Object), bet es aplūkošu dažus no izplatītākajiem jautājumiem, kas rodas, kad tie ir nav ieviesti vai netiek pareizi ieviesti. Ar šīm demonstrācijām es arī mēģinu parādīt, kāpēc ir svarīgi veikt rūpīgu kodu pārskatīšanu, rūpīgu vienību testēšanu un / vai uz rīku balstītu analīzi, lai pārbaudītu šo metožu ieviešanas pareizību.
Tā kā visi Java objekti galu galā pārmanto ieviešanu vienāds (objekts)
un hashCode ()
, Java kompilators un patiešām Java izpildlaika palaišanas programma neziņos par problēmām, atsaucoties uz šīm šo metožu "noklusējuma ieviešanām". Diemžēl, kad šīs metodes ir nepieciešamas, šo metožu noklusējuma ieviešana (piemēram, viņu brālēns toString metode) reti tiek vēlēta. Javadoc balstītā objektu klases API dokumentācijā ir apspriests "līgums", kas sagaidāms no jebkuras programmas ieviešanas vienāds (objekts)
un hashCode ()
metodes, kā arī tiek apspriesta katra iespējamā noklusējuma ieviešana, ja to neievēro bērnu klases.
Piemēriem šajā ziņojumā es izmantoju klasi HashAndEquals, kuras kodu saraksts tiek parādīts blakus dažādu objektu personu objektu parādīšanai ar atšķirīgu atbalsta līmeni hashCode
un ir vienāds
metodes.
HashAndEquals.java
iepakojums dustin.piemēri; importēt java.util.HashSet; importēt java.util.Set; importēt statisko java.lang.System.out; publiskā klase HashAndEquals {private static final String HEADER_SEPARATOR = "========================================= =============================== "; privāta statiska gala int HEADER_SEPARATOR_LENGTH = HEADER_SEPARATOR.length (); privāta statiska galīgā virkne NEW_LINE = System.getProperty ("line.separator"); privāta galīgā persona persona1 = jauna persona ("Flintstone", "Fred"); privāta galīgā persona persona2 = jauna persona ("drupa", "Bārnijs"); privāta galīgā persona persona3 = jauna persona ("Flintstone", "Fred"); privāta galīgā persona persona4 = jauna persona ("drupa", "Bārnijs"); public void displayContents () {printHeader ("OBJEKTU SATURS"); out.println ("Persona 1:" + persona1); out.println ("Persona 2:" + persona2); out.println ("Persona 3:" + persona3); out.println ("Persona 4:" + persona4); } public void CompareEquality () {printHeader ("VIENLĪDZĪBAS SALĪDZINĀJUMI"); out.println ("Persona1.vienāda (Persona2):" + persona1.vienāda (persona2)); out.println ("Persona1.vienāda (Persona3):" + persona1.vienāda (persona3)); out.println ("Persona2.vienāda (Persona4):" + persona2.vienāda (persona4)); } public void CompareHashCodes () {printHeader ("SALĪDZINĀT HASH KODUS"); out.println ("Person1.hashCode ():" + personai1.hashCode ()); out.println ("Persona2.hashCode ():" + persona2.hashCode ()); out.println ("Person3.hashCode ():" + person3.hashCode ()); out.println ("Person4.hashCode ():" + person4.hashCode ()); } public Set addToHashSet () {printHeader ("PIEVIENOT IESTATAMOS ELEMENTUS - VAI PIEVIENOTI VAI VIENI?"); galīgais komplekts = jauns HashSet (); out.println ("Set.add (Persona1):" + set.add (Persona1)); out.println ("Set.add (Persona2):" + set.add (Persona2)); out.println ("Set.add (Persona3):" + set.add (persona3)); out.println ("Set.add (Person4):" + set.add (person4)); atgriešanās komplekts; } public void removeFromHashSet (final Set sourceSet) {printHeader ("NOŅEMT ELEMENTUS NO KOMPLEKTA - VAI ATTIECĪBĀ UZ TIKS NOZĪMĒT?"); out.println ("Set.remove (Person1):" + sourceSet.remove (person1)); out.println ("Set.remove (Person2):" + sourceSet.remove (person2)); out.println ("Set.remove (Person3):" + sourceSet.remove (person3)); out.println ("Set.remove (Person4):" + sourceSet.remove (person4)); } public static void printHeader (final String headerText) {out.println (NEW_LINE); out.println (HEADER_SEPARATOR); out.println ("=" + headerText); out.println (HEADER_SEPARATOR); } public static void main (final String [] argumenti) {final HashAndEquals instance = new HashAndEquals (); instance.displayContents (); instance.compareEquality (); instance.compareHashCodes (); gala komplekts = instance.addToHashSet (); out.println ("Iestatīt pirms noņemšanas:" + iestatīt); //instance.person1.setFirstName("Bam Bam "); instance.removeFromHashSet (kopa); out.println ("Iestatīt pēc noņemšanas:" + iestatīt); }}
Iepriekš minētā klase tiks atkārtoti izmantota kā - ar vienu nelielu izmaiņu vēlāk amatā. Tomēr Persona
klase tiks mainīta, lai atspoguļotu ir vienāds
un hashCode
un parādīt, cik viegli var tos sajaukt, vienlaikus grūti izsekot problēmai, ja ir kļūda.
Nav skaidra ir vienāds
vai hashCode
Metodes
Pirmā versija Persona
klase nenodrošina nepārprotamu ignorētu versiju ir vienāds
metode vai hashCode
metodi. Tas parādīs katras no šīm mantotajām metodēm "noklusējuma ieviešanu" Objekts
. Šeit ir koda avots Persona
bez hashCode
vai ir vienāds
nepārprotami tiek ignorēts.
Person.java (nav izteikta hashCode vai vienāda ar metodi)
iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāts fināls String firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public String toString () {return this.firstName + "" + this.lastName; }}
Šī pirmā versija Persona
nenodrošina get / set metodes un nenodrošina ir vienāds
vai hashCode
ieviešana. Kad galvenā demonstrācijas klase HashAndEquals
tiek izpildīts ar šī gadījuma gadījumiem ir vienāds
-bez un hashCode
bez Persona
klasē, rezultāti parādās, kā parādīts nākamajā ekrāna momentuzņēmumā.
No iepriekš redzamās izejas var izdarīt vairākus novērojumus. Pirmkārt, bez skaidras ANO ieviešanas vienāds (objekts)
metodi, neviens no Persona
tiek uzskatīti par vienādiem, pat ja visi gadījumu (divu virkņu) atribūti ir identiski. Tas ir tāpēc, ka, kā paskaidrots Object.equals (Object) dokumentācijā, noklusējums ir vienāds
ieviešanas pamatā ir precīza atsauces atbilstība:
Otrais novērojums no šī pirmā piemēra ir tāds, ka hash kods katram Persona
objekts pat tad, ja diviem gadījumiem visiem to atribūtiem ir vienādas vērtības. HashSet atgriežas taisnība
kad kopai tiek pievienots "unikāls" objekts (HashSet.add) vai nepatiesa
ja pievienotais objekts netiek uzskatīts par unikālu un tāpēc netiek pievienots. Līdzīgi HashSet
atgriež metodi noņemt taisnība
ja sniegtais objekts tiek uzskatīts par atrastu un noņemtu vai nepatiesa
ja tiek uzskatīts, ka norādītais objekts nav HashSet
un tāpēc to nevar noņemt. Tāpēc ka ir vienāds
un hashCode
iedzimtas noklusējuma metodes izturas pret šiem gadījumiem kā pilnīgi atšķirīgus, nav pārsteigums, ka visi tiek pievienoti kopai un visi tiek veiksmīgi noņemti no kopas.
Skaidrs ir vienāds
Tikai metode
Programmas otrā versija Persona
klase ietver nepārprotami ignorētu ir vienāds
metodi, kā parādīts nākamajā kodu sarakstā.
Person.java (paredzēta vienāda metode)
iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāts fināls String firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public Boolean equals (Object obj) {if (obj == null) {return false; } ja (tas == obj) {atgriezties taisnība; } if (this.getClass ()! = obj.getClass ()) {return false; } gala Persona cita = (Persona) obj; ja (tas.pavardesNosaukums == null? cits.pavardisNosaukums! = null:! tas.pavardasNav.vienaks (cits.pavardelesName)) {atgriež nepatiesu; } if (this.firstName == null? other.firstName! = null:! this.firstName.equals (other.firstName)) {return false; } return true; } @Orride public String toString () {return this.firstName + "" + this.lastName; }}
Kad gadījumi Persona
ar vienāds (objekts)
tiek izmantoti skaidri definēti, izeja ir tāda, kā parādīts nākamajā ekrāna momentuzņēmumā.
Pirmais novērojums ir tāds, ka tagad ir vienāds
aicina Persona
gadījumi patiešām atgriežas taisnība
kad objekts ir vienāds, ja visi atribūti ir vienādi, nevis pārbauda stingru atsauces vienādību. Tas parāda, ka paradums ir vienāds
ieviešana Persona
ir paveicis savu darbu. Otrais novērojums ir tas, ka ir vienāds
metode nav ietekmējusi spēju pievienot un noņemt šķietami to pašu objektu HashSet
.
Skaidrs ir vienāds
un hashCode
Metodes
Ir pienācis laiks pievienot nepārprotamu hashCode ()
metodi Persona
klasē. Patiešām, tas tiešām bija jādara, kad ir vienāds
metode tika ieviesta. Iemesls tam ir norādīts programmas dokumentācijā Object.equals (Object)
metode:
Šeit ir Persona
ar skaidri īstenotu hashCode
metode, kuras pamatā ir tie paši Persona
kā ir vienāds
metodi.
Person.java (tiešs vienāds ar hashCode ieviešanu)
iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāts fināls String firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public int hashCode () {return lastName.hashCode () + firstName.hashCode (); } @Orride public Boolean equals (Object obj) {if (obj == null) {return false; } ja (tas == obj) {atgriezties taisnība; } if (this.getClass ()! = obj.getClass ()) {return false; } gala Persona cita = (Persona) obj; ja (tas.pavardesNosaukums == null? cits.pavardisNosaukums! = null:! tas.pavardasNav.vienaks (cits.pavardelesName)) {atgriež nepatiesu; } if (this.firstName == null? other.firstName! = null:! this.firstName.equals (other.firstName)) {return false; } return true; } @Orride public String toString () {return this.firstName + "" + this.lastName; }}
Rezultāts, darbojoties ar jauno Persona
klase ar hashCode
un ir vienāds
metodes tiek parādītas tālāk.
Nav pārsteidzoši, ka hash kodi, kas atgriezti objektiem ar vienādām atribūtu vērtībām, tagad ir vienādi, taču interesantāks novērojums ir tas, ka mēs varam tikai divus no četriem gadījumiem pievienot HashSet
tagad. Tas ir tāpēc, ka trešais un ceturtais pievienošanas mēģinājums tiek uzskatīts par mēģinājumu pievienot objektu, kas jau tika pievienots kopai. Tā kā bija tikai divi pievienoti, var atrast un noņemt tikai divus.
Problēmas ar maināmiem hashCode atribūtiem
Attiecībā uz ceturto un pēdējo piemēru šajā amatā es skatos, kas notiek, kad hashCode
ieviešanas pamatā ir atribūts, kas mainās. Šajā piemērā: a setFirstName
metode tiek pievienota Persona
un galīgais
modifikators tiek noņemts no tā vārds
atribūts. Turklāt galvenajai HashAndEquals klasei ir jānoņem komentārs no rindas, kas izsauc šo jauno kopas metodi. Jaunā versija Persona
tiek parādīts nākamais.
iepakojums dustin.piemēri; public class Persona {private final Stīgu uzvārds; privāta virkne firstName; publiska persona (galīgā virkne newLastName, galīgā virkne newFirstName) {this.lastName = newLastName; this.firstName = newFirstName; } @Orride public int hashCode () {return lastName.hashCode () + firstName.hashCode (); } public void setFirstName (galīgā virkne newFirstName) {this.firstName = newFirstName; } @Orride public Boolean equals (Object obj) {if (obj == null) {return false; } ja (tas == obj) {atgriezties taisnība; } if (this.getClass ()! = obj.getClass ()) {return false; } gala Persona cita = (Persona) obj; ja (tas.pavardesNosaukums == null? cits.pavardisNosaukums! = null:! tas.pavardasNav.vienaks (cits.pavardelesName)) {atgriež nepatiesu; } if (this.firstName == null? other.firstName! = null:! this.firstName.equals (other.firstName)) {return false; } return true; } @Orride public String toString () {return this.firstName + "" + this.lastName; }}
Rezultāts, kas iegūts, izpildot šo piemēru, tiek parādīts nākamais.