Возьмём для примера объект класса Nokia3310. Если попытаться представить его как строку, результат будет выглядеть печально:
System.out.println("мой телефон: " + new Nokia3310("valmistusnumero"));
// мой телефон: Nokia3310@4b67cf4d
На объекте был неявно вызван метод toString(), определённый в классе Object:
package java.lang;
/**
* Class {@code Object} is the root of the class hierarchy.
* Every class has {@code Object} as a superclass. All objects,
* including arrays, implement the methods of this class.
*
* @author unascribed
* @see java.lang.Class
* @since JDK1.0
*/
public class Object {
...
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
Nokia3310, как и все остальные классы, неявно наследует класс Object. Это значит, что методы и поля, которые объявлены в Object, доступны также и в Nokia3310. Это явление (наследование классов) называется наследованием реализации (implementation inheritance).
Чтобы сделать результат приведения Nokia3310 к строке более осмысленным, нужно переопределить метод toString().
public final class Nokia3310 implements MobilePhone {
...
public String toString() {
return "Nokia3310(заряжен на " + getChargePercentage() + "%)";
}
}
Снова попробуем привести к строке:
мой телефон: Nokia3310(заряжен на 42%)
Переопределение toString() считается хорошим тоном, т. к. помогает при отладке кода. В toString() крайне желательно упоминать все поля, например, так:
public String toString() {
return "Nokia3310(IMEI=" + imei +
", serialNumber=" + serialNumber +
", batteryVoltage=" + batteryVoltage + ")";
}
// мой телефон: Nokia3310(IMEI=7660895798011981084, serialNumber=valmistusnumero, batterVoltage=3.8)
Аннотация @Override
Порой мы делаем опечатки. Например, если написать asString вместо toString, получится метод, который не переопределяет toString.
Аннотация @Override гарантирует, что описанный после неё метод переопределяет метод родителя.
Эта аннотация должна стоять на всех методах, которые переопределяют методы родительского класса или интерфейса:
public final class Nokia3310 implements MobilePhone {
...
@Override
public void dial(String number) {
...
}
@Override
public int getChargePercentage() {
...
}
@Override
public String toString() {
...
}
}
Если же попытаться переопределить метод, которого в суперклассе нет, аннотация @Override поможет сразу же найти ошибку:
Наследование реализации
К сожалению,
Как показано выше,
Java позволяет классам наследовать классы. Таким образом, класс может иметь как собственные, явно определённые, так и унаследованные методы и поля.
Всё, что хочется сказать о наследовании классов, — что это достаточно ненадёжная техника. Подробнее — см. Joshua Bloch, Effective Java, 2nd Edition, Item 16 'Favor composition over inheritance'.