// пример вызовет ошибку компиляции class Test { int get() { return 5; } Point get() { return new Point(3,5); } void print(int x) { System.out.println('it's int! '+x); } void print(Point p) { System.out.println('it's Point! '+p.x+ ', '+p.y); } public static void main (String s[]) { Test t = new Test(); t.print(t.get()); // Двусмысленность! } }
В классе определена запрещенная пара методов get() с одинаковыми сигнатурами и различными возвращаемыми значениями. Обратимся к выделенной строке в методе main, где возникает конфликтная ситуация, с которой компилятор не может справиться. Определены два метода print() (у них разные аргументы, а значит, и сигнатуры, то есть это допустимые методы ), и чтобы разобраться, какой из них будет вызван, нужно знать точный тип возвращаемого значения метода get(), что невозможно.
На основе этого примера можно понять, как составлено понятие сигнатуры. Действительно, при вызове указывается имя метода и перечисляются его аргументы, причем компилятор всегда может определить их тип. Как раз эти понятия и составляют сигнатуру, и требование ее уникальности позволяет компилятору всегда однозначно определить, какой метод будет вызван.
Точно так же в предыдущем примере вторая пара методов различается именем аргументов, которые также не входят в определение сигнатуры и не позволяют определить, какой из двух методов должен быть вызван.
Аналогично, третья пара различается лишь модификаторами доступа, что также недопустимо.
Наконец, завершает заголовок метода throws -выражение. Оно применяется для корректной работы с ошибками в Java и будет подробно рассмотрено в соответствующей лекции.
Пример объявления метода:
public final java.awt.Point createPositivePoint(int x, int y) throws IllegalArgumentException { return (x>0 && y>0) ? new Point(x, y) : null; }
Далее, после заголовка метода следует тело метода. Оно может быть пустым и тогда записывается одним символом 'точка с запятой'. Native - методы всегда имеют только пустое тело, поскольку настоящая реализация написана на другом языке.
Обычные же методы имеют непустое тело, которое описывается в фигурных скобках, что показано в многочисленных примерах в этой и других лекциях. Если текущая реализация метода не выполняет никаких действий, тело все равно должно описываться парой пустых фигурных скобок:
public void empty() {}
Если в заголовке метода указан тип возвращаемого значения, а не void, то в теле метода обязательно должно встречаться return -выражение. При этом компилятор проводит анализ структуры метода, чтобы гарантировать, что при любых операторах ветвления возвращаемое значение будет сгенерировано. Например, следующий пример является некорректным:
// пример вызовет ошибку компиляции public int get() { if (condition) { return 5; } }
Видно, что хотя тело метода содержит return -выражение, однако не при любом развитии событий возвращаемое значение будет сгенерировано. А вот такой пример является верным:
public int get() { if (condition) { return 5; } else { return 3; } }
Конечно, значение, указанное после слова return, должно быть совместимо по типу с объявленным возвращаемым значением (это понятие подробно рассматривается в лекции 7).
В методе без возвращаемого значения (указано void ) также можно использовать выражение return без каких-либо аргументов. Его можно указать в любом месте метода и в этой точке выполнение метода будет завершено: