Методы классов в Java
Добавляйте методы экземпляра в классы Java, чтобы задать поведение объектам и работать с их полями.
Методы класса определяют поведение его объектов. Поля описывают то, чем объект является; методы — то, что он умеет делать. До сих пор вы писали методы static — утилиты, которые принадлежат самому классу. Эта глава посвящена методам экземпляра, которые принадлежат каждому объекту и работают с его полями.
Термин «методы класса» в Java трактуется по-разному. Большинство авторов понимают под ним любые методы, объявленные внутри класса — как статические, так и нестатические. Некоторые используют его строго для обозначения static-методов (поскольку они буквально принадлежат классу). Здесь мы рассмотрим методы экземпляра, а static-методам посвящена отдельная глава — Java static.
Метод экземпляра
Добавьте метод в тело класса без ключевого слова static, и он станет частью каждого объекта:
public class Circle {
double radius;
double area() {
return Math.PI * radius * radius;
}
}Теперь каждый созданный объект Circle может вычислить area():
Circle c = new Circle();
c.radius = 5;
System.out.println(c.area()); // 78.539...В теле метода radius используется без уточнения — Java разрешает это имя как this.radius, поле того объекта, на котором был вызван метод.
this — получатель
У каждого метода экземпляра есть невидимый параметр this, указывающий на объект, на котором был вызван метод:
Circle a = new Circle(); a.radius = 2;
Circle b = new Circle(); b.radius = 5;
a.area(); // inside area(), this == a, so this.radius == 2
b.area(); // inside area(), this == b, so this.radius == 5Один и тот же скомпилированный метод выполняется в обоих случаях; меняется лишь то, на какой объект ссылается this. В главе ключевое слово this подробно рассматривается, когда нужно явно писать this., а когда можно опустить.
Методы, изменяющие состояние
Метод также может изменять поля. Такие методы часто называют мутаторами:
public class Counter {
int count;
void increment() {
count++; // mutates this.count
}
void reset() {
count = 0;
}
int get() {
return count;
}
}
Counter c = new Counter();
c.increment(); c.increment(); c.increment();
System.out.println(c.get()); // 3Изменение через методы — это то, как объекты эволюционируют со временем, не позволяя внешнему коду напрямую трогать их поля. Методы, которые только читают поле и возвращают его значение, — это зеркальное отражение: их обычно называют аксессорами или геттерами (int get() выше — пример такого). Направление чтения и записи через методы, вместо прямого доступа к полям, — это основная идея инкапсуляции: объект сам управляет своими данными.
Вызов других методов изнутри
Методы экземпляра могут вызывать другие методы того же объекта просто по имени:
public class Rectangle {
double width, height;
double area() { return width * height; }
double perimeter() { return 2 * (width + height); }
String describe() {
return "area=" + area() + ", perimeter=" + perimeter();
}
}Компилятор превращает area() и perimeter() внутри describe в this.area() и this.perimeter(). Они вызываются на том прямоугольнике, на котором был вызван метод describe.
Static или метод экземпляра — что выбрать?
Эмпирическое правило: зависит ли метод от состояния конкретного объекта?
- Если да — это метод экземпляра (без
static). - Если нет — это
static.
public class Math2 {
public static int squared(int n) { // pure calculation — static
return n * n;
}
}
public class Counter {
int count;
public void increment() { // reads/writes this.count — instance
count++;
}
}Частый признак проблемы — метод экземпляра, который полностью игнорирует this. Такой метод — это static, замаскированный под метод экземпляра. В главе Java static разбираются компромиссы.
Для вызова метода экземпляра нужен объект
Поскольку методы экземпляра читают this, вызвать такой метод без объекта невозможно:
Counter.increment(); // ERROR — increment is not static
new Counter().increment(); // fine — increment is called on a fresh Counter (which is then thrown away)
Counter c = new Counter();
c.increment(); // fine — increment is called on cЭто самая частая ошибка компиляции у начинающих: нестатический метод вызывается из статического контекста (как правило, из main, который сам является статическим).
Если компилятор сообщает non-static method increment() cannot be referenced from a static context, вы почти наверняка вызвали метод экземпляра без объекта — например, Counter.increment() вместо c.increment(). Сначала создайте объект, затем вызывайте метод на нём.
Перегрузка и переопределение
Методы экземпляра поддерживают оба механизма:
- Перегрузка: несколько методов с одинаковым именем, но разными списками параметров в одном классе (рассматривается в главе о перегрузке методов).
- Переопределение: подкласс заменяет унаследованную версию метода (рассматривается в главе о переопределении методов).
С перегрузкой вы уже сталкивались в части 5. Переопределение — одно из главных преимуществ наследования, и мы вернёмся к нему совсем скоро.
Тело метода — это блок
Тело метода — это обычный блок кода: объявленные внутри переменные локальны для вызова, управляющие конструкции работают так же, как и везде, и из него можно выйти досрочно. То, что код находится внутри класса, никак не влияет на то, как читается тело. Единственная дополнительная возможность — «голые» идентификаторы теперь могут ссылаться на поля объекта и его другие методы.
Практический пример
Этот пример объединяет в одном классе аксессоры (area, perimeter), мутатор (scale) и метод, вызывающий другие методы (describe). Rectangle объявлен как static class только для того, чтобы весь демо-код уместился в один файл — это ключевое слово относится к вложенному классу, но не к его методам экземпляра, которые по-прежнему выполняются на конкретном прямоугольнике. Обратите внимание: масштабирование r не затрагивает s — каждый объект хранит собственное состояние.
Что дальше
До сих пор вы создавали объекты командой new Dog(), а затем заполняли поля построчно. Это многословно, чревато ошибками — и легко что-то забыть. Решение — конструктор, специальный метод, который выполняется при создании объекта. Переходите к главе о конструкторах.