Кен Арнольд Джеймс Гослинг

Вид материалаДокументы

Содержание


2.6.1. Значения параметров
2.6.2. Применение методов для ограничения доступа
Подобный материал:
1   ...   8   9   10   11   12   13   14   15   ...   81

2.6. Методы


Методы класса обычно содержат код, который анализирует состояние объекта и изменяет его. Некоторые классы имеют поля public, к которым программисты могут обращаться напрямую, но в большинстве случаев такой подход оказывается не слишком удачным (см. “Проектирование расширяемого класса”). Многие классы обладают функциями, которые невозможно свести к чтению или изменению некоторой величины — для них необходимы вычисления.

Вызов метода представляет собой операцию, выполняемую с объектом посредством ссылки на него с использованием оператора:

reference.method(parameters)

Каждый метод вызывается с определенным количеством параметров. Java не поддерживает методов, у которых допускается переменное число параметров. Каждый параметр имеет строго определенный тип — примитивный или ссылочный. Кроме того, методы обладают типом возвращаемого значения, который указывается перед их именем. Например, приведем метод класса Body, который создает строку типа String с описанием конкретного объекта Body:

public String toString() {

String desc = idNum + “ (” + name + “)”;

if (orbits != null)

desc += “ orbits ” + orbits.toString();

return desc;

}


В этом методе производится конкатенация объектов String с помощью операторов + и +=. Сначала образуется строка, содержащая идентификатор и название объекта. Если данное небесное тело обращается вокруг другого, то к ней присоединяется строка с описанием центра вращения, для чего вызывается метод toString соответствующего объекта. Последовательность рекурсивных вызовов продолжает строить цепочку тел, обращающихся вокруг друг друга, пока не будет найдено тело, не имеющее центра вращения.

Метод toString не совсем обычен. Если у объекта имеется метод с именем toString, который вызывается без параметров и возвращает значение типа String, то он используется для приведения объекта к типу String, если он участвует в конкатенации строк, выполняемой оператором +. В следующих выражениях:

System.out.println(“Body ” + sun);

System.out.println(“Body ” + earth);

происходит косвенный вызов методов toString для объектов sun и earth, приводящий к следующим результатам:

Body 0 (Sol)

Body 1 (Earth) orbits 0 (Sol)

Существует несколько способов, которыми удается добиться возвращения методом нескольких значений: можно возвращать ссылку на объект, в котором эти значения хранятся в виде полей; принимать в качестве параметров ссылки на объекты, в которых должны сохраняться результаты; наконец, можно возвращать массив с результатами.

К примеру, предположим, что вам нужно написать метод для возвращения перечня финансовых операций, которые определенное лицо может выполнять с банковским счетом. Таких операций может быть несколько (зачисление и снятие средств со счета и т. д.); следовательно, метод должен возвращать несколько значений. Вот как выглядит объект Permissions, в котором сохраняются логические значения, определяющие допустимость той или иной операции:

class Permissions {

public boolean canDeposit,

canWithdraw,

canClose;

}

А вот как выглядит метод, заполняющий эти поля:

class Account {

public Permissions permissionsFor(Person who) {

Permissions perm = new Permissions();

perm.canDeposit = canDeposit(who);

perm.canWithdraw = canWithdraw(who);

perm.canClose = canClose(who);

return perm;

}


// ... определение метода canDeposit()

}


Если метод не возвращает никакого значения, то на месте возвращаемого типа ставится ключевое слово void. В противном случае каждый возможный путь выполнения его операторов должен возвращать значение, которое может быть присвоено переменной объявленного типа. К примеру, метод permissions For не может возвращать значение типа String, поскольку невозможно присвоить объект типа String переменной типа Permissions. Однако вы можете объявить, что метод permissionsFor возвращает значение String, не изменяя при этом оператор return, поскольку ссылка на объект Permissions может быть присвоена переменной типа Object.

2.6.1. Значения параметров


Все параметры в Java передаются “по значению”. Другими словами, значения переменных-параметров метода являются копиями значений, указанных при его вызове. Если передать методу переменную некоторого типа, то параметр будет представлять собой копию этой переменной; ее изменение внутри метода никак не повлияет на значение переменной в коде за его пределами. Например:

class PassByValue {

public static void main(String[] args) {

double one = 1.0;


System.out.println(“before: one = ” + one);

halveIt(one);

System.out.println(“after: one = ” + one);

}


public static void halveIt(double arg) {

arg /= 2.0; //

System.out.println(“halved: arg = ” + arg);

}

}


Приведенные ниже результаты показывают, что деление на два переменной arg в методе halveIt не меняет значения переменной one в методе main:

before: one = 1

halved: arg = 0.5

after: one = 1


Однако, если параметр метода представляет собой ссылку на объект, то “по значению” передается ссылка, а не сам объект! Следовательно, вы можете изменять в методе тот объект, на который она ссылается, — значение ссылки остается прежним. Можно изменять любые поля объекта или вызывать методы, влияющие на его состояние, — произойдет изменение объекта во всех фрагментах программы, где имеется ссылка на него. Приведенный ниже пример наглядно показывает, чем данный случай отличается от предыдущего:

class PassRef {

public static void main(String[] args) {

Body sirius = new Body(“Sirius”, null);


System.out.println(“before: ” + sirius);

commonName(sirius);

System.out.println(“after: ” + sirius);

}


public static void commonName(Body bodyRef) {

bodyRef.name = “Dog Star”;

bodyRef = null;

}

}

Результат будет следующим:

before: 0 (Sirius)

after: 0 (Dog Star)


Обратите внимание на то, что название объекта изменилось, тогда как ссылка bodyRef все равно указывает на объект Body (хотя метод commonName присваивал параметру bodyRef значение null).



Приведенная выше диаграмма показывает состояние ссылок непосредственно после вызова commonName в main. Обе ссылки — sirius (в main) и bodyRef (в commonName) — указывают на один и тот же объект. Когда commonName изменяет значение поля bodyRef.name, то название изменяется в объекте, совместно используемом обоими ссылками. Однако при присвоении null ссылке bodyRef изменяется только ее значение, тогда как значение ссылки на объект sirius остается тем же самым; вспомним, что параметр bodyRef является передаваемой по значению копией sirius. Внутри метода commonName изменяется лишь значение переменной-параметра bodyRef, подобно тому как в методе halveIt изменялось лишь значение параметра-переменной arg. Если бы изменение bodyRef относилось и к значению sirius в main, то в строке, начинающейся с “after: ”, стояло бы “null”. Тем не менее переменные bodyRef в commonName и sirius в main указывают на один и тот же объект, поэтому изменения, вносимые в commonName, отражаются и в том объекте, на который ссылается sirius.

2.6.2. Применение методов для ограничения доступа


Пользоваться классом Body с несколькими конструкторами стало значительно удобнее, чем его старым вариантом, состоявшим из одних данных; кроме того, мы обеспечили правильное автоматическое присвоение значений idNum. И все же программист может все испортить, изменяя значение поля idNum после конструирования объекта, — ведь данное поле объявлено как public и открыто для любых действий. Необходимо, чтобы поле idNum содержало данные, доступные только для чтения. Подобные данные в объектах встречаются довольно часто, но в языке Java не существует ключевого слова, которое сделало бы поле за пределами класса доступным только для чтения.

Чтобы сделать поле доступным только для чтения, мы должны скрыть его. Для этого поле idNum объявляется с ключевым словом private, а в класс добавляется новый метод, с помощью которого код за пределами класса может получить значение этого поля:

class Body {

private long idNum; // поле стало private


public String name = “”;

public Body orbits = null;

private static long nextID = 0;


Body() {

idNum = nextID++;

}

public long id() {

return idNum;

}


// ...

}


Начиная с этого момента программист, которому понадобилось узнать идентификатор небесного тела, должен вызвать метод id, возвращающий требуемое значение. У программиста не остается никакой возможности изменить идентификатор — в сущности, за пределами класса его можно рассматривать как величину, доступную только для чтения. Данное поле может быть изменено только внутренними методами класса Body.

Методы, регулирующие доступ к внутренним данным класса, иногда называются методами доступа (accessor methods). С их помощью также можно (и, наверное, нужно) защитить поля name и orbits.

Даже если интересы приложения и не требуют полей, доступных только для чтения, объявление полей класса с ключевым словом private и создание методов для присвоения/получения их значений позволяет вам определить необходимые действия над объектом в будущем. Если программист может непосредственно обращаться к полям класса, вы неизбежно теряете контроль над тем, какие значения будут принимать эти поля и что происходит в программе при их изменении. По этим причинам в дальнейших примерах этой книги поля public встречаются очень редко.

Упражнение 2.8

Объявите поля класса Vehicle с ключевым словом private и опишите соответствующие методы доступа. Для каких полей следует предусмотреть методы, изменяющие их значения, а для каких — нет?

Упражнение 2.9

Объявите поля класса LinkedList с ключевым словом private и включите в класс соответствующие методы доступа. Для каких полей следует предусмотреть методы, изменяющие их значения, а для каких — нет?

Упражнение 2.10

Включите в класс Vehicle метод changeSpeed для задания текущей скорости машины в соответствии с передаваемым значением и метод stop для обнуления скорости.

Упражнение 2.11

Включите в класс LinkedList метод для подсчета количества элементов в списке.