Any Object
is an Object
or a child of an Object
as described in the Java Language Specification:
The class Object is a super class (§8.1) of all other classes. A variable of type Object can hold a reference to the null reference or to any object, whether it is an instance of a class or an array (§10). All class and array types inherit the methods of class Object[…]
The Object implements the following methods:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package java.lang; public class Object { public final Class<?> getClass() { ... } public String toString() { ... } public boolean equals(Object obj) { ... } public int hashCode() { ... } protected Object clone() public final void wait() public final void wait(long millis) public final void wait(long millis, int nanos) { ... } public final void notify() { ... } public final void notifyAll() { ... } protected void finalize() } |
I want to elaborate on the methods public String toString()
, public boolean equals(Object obj)
, public int hashCode()
. I want to clear what are they for and when and how you should overwrite them. All of these methods are not final and can be overwritten.
Let’s start with public String toString()
.
toString()
is mostly called implicit (internal without your assistance) but can also called explicit (with your assistance).
A good example for an implicit call is System.out.println()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class Example { class Person { private String name; private int age; public Person(final String name, final int age) { setName(name); setAge(age); } private void setName(String name) { this.name = name; } private void setAge(int age) { this.age = age; } } public static void main(String[] args) { Example m = new Example(); Person alf = m.new Person("Alf", 32); System.out.println(alf); } } |
alf.toString()
is actually called/searched in Person
but not found. So the procedure is repeated in the next closer parent class until the method is found (in this case Object, because Person has no base class. You could also read this as Person extends Object
). This mechanism is called Dynamic Binding.
This example would print something like equalsContract.Example$Person@dc8569
(it’s a combination of the classname and the hashCode) which is not very useful. We could approve this example by overwriting the toString() method by our own. So we could print something more meaningful.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public class Example { class Person { private String name; private int age; public Person(final String name, final int age) { setName(name); setAge(age); } private void setName(String name) { this.name = name; } private void setAge(int age) { this.age = age; } @Override public String toString() { return "name='" +name+ "', age='" +age+ "'"; } } public static void main(String[] args) { Example m = new Example(); Person alf = m.new Person("Alf", 32); System.out.println(alf); } } |
The new toString()
method returns now information about the object state. By overwriting this method the call alf.toString()
is successfully now.
The output is: name='Alf', age='32'
.
Info
I recommend to overwrite the toString() method – this gives you more easy readable information about the object state.
The boolean equals(Object obj)
is very important in combination with the java collections framework. For example if you would like to check if an collection contains an specific object:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | package equalsContract; import java.util.ArrayList; import java.util.Collection; public class Example { Collection personList = new ArrayList(); class Person { private String name; private int age; public Person(final String name, final int age) { setName(name); setAge(age); } private void setName(String name) { this.name = name; } private void setAge(int age) { this.age = age; } @Override public String toString() { return "name='" +name+ "', age='" +age+ "'"; } } public static void main(String[] args) { Example m = new Example(); Person alf = m.new Person("Alf", 32); System.out.println(alf); // Add alf to list m.personList.add(alf); // search for Alf boolean contains = m.personList.contains(m.new Person("Alf", 32)); System.out.println(contains); } } |
contains(Object o)
would call the default implementation of equals(Object o)
for each item. Each item in the list would be compared with the passed Object. The composition is done by calling the equals(Object o)
method. Since each object is unique in Java this Person would not be found.
But we could overwrite the equals() method with the knowledge of what makes a Person unique.
Javadoc
The javadoc of the equals functions lists various specifications about what the equals function must cover (the equals contract): Indicates whether some other object is “equal to” this one.
- It is reflexive: for any non-null reference value x, x.equals(x) should return true.
- It is symmetric: for any non-null reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- It is transitive: for any non-null reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
- It is consistent: for any non-null reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the objects is modified.
- For any non-null reference value x, x.equals(null) should return false.
With this knowledge we can overwrite the equals method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | package equalsContract; import java.util.ArrayList; import java.util.Collection; public class Example { Collection personList = new ArrayList(); class Person { private String name; private int age; public Person(final String name, final int age) { setName(name); setAge(age); } private void setName(String name) { this.name = name; } private void setAge(int age) { this.age = age; } @Override public String toString() { return "name='" +name+ "', age='" +age+ "'"; } @Override public boolean equals(Object o) { if(o == null) return false; if(this == o) return true; if(this.getClass() != o.getClass()) return false; if(this.name.equals(((Person) o).name) && this.age.equals(((Person) o).age)) return true; // any other case return false; } } public static void main(String[] args) { Example m = new Example(); Person alf = m.new Person("Alf", 32); System.out.println(alf); // Add alf to list m.personList.add(alf); // search for Alf boolean contains = m.personList.contains(m.new Person("Alf", 32)); System.out.println(contains); } } |
Now the person was found in the list. The output is true
.
Sources:
http://docs.oracle.com/javase/specs/jls/se5.0/html/typesValues.html
Book – Der Weg zum Java-Profi http://dpunkt.de/buecher/3810/der-weg-zum-java-profi.html