// пример вызовет ошибку компиляции
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 без каких-либо аргументов. Его можно указать в любом месте метода и в этой точке выполнение метода будет завершено:
public void calculate(int x, int y) {
if (x<=0 || y<=0) {
return;
// некорректные входные
// значения, выход из метода
}
... // основные вычисления
}
Выражений return (с параметром или без для методов с/без возвращаемого значения) в теле одного метода может быть сколько угодно. Однако следует помнить, что множество точек выхода в одном методе может заметно усложнить понимание логики его работы.
Объявление конструкторов
Формат объявления конструкторов похож на упрощенное объявление методов. Также выделяют заголовок и тело конструктора. Заголовок состоит, во-первых, из модификаторов доступа (никакие другие модификаторы недопустимы). Во-вторых, указывается имя класса, которое можно расценивать двояко. Можно считать, что имя конструктора совпадает с именем класса. А можно рассматривать конструктор как безымянный, а имя класса – как тип возвращаемого значения, ведь конструктор может породить только объект класса, в котором он объявлен. Это исключительно дело вкуса, так как на формате объявления никак не сказывается:
public class Human {
private int age;
protected Human(int a) {
age=a;
}
public Human(String name, Human mother,
Читать дальше