Перегрузка методов. Перегрузка конструкторов. Использование объектов в качестве параметров
Перегрузка методов Java разрешает определение внутри одного класса двух или более методов с одним именем, если только объявления их параметров различны. В этом случае методы называют перегруженными, а процесс – перегрузкой методов. Перегрузка методов – один из способов поддержки полиморфизма в Java. Тем читателям, которые никогда не использовали язык, допускающий перегрузку методов, эта концепция вначале может показаться странной. Но, как вы вскоре убедитесь, перегрузка методов – одна из наиболее впечатляющих и полезных функциональных возможностей Java. При вызове перегруженного метода для определения нужной версии Java использует тип и/или количество аргументов метода. Следовательно, перегруженные методы должны различаться по типу и/или количеству их параметров. Хотя возвращаемые типы перегруженных методов могут быть различны, самого возвращаемого типа не достаточно для различения двух версий метода. Когда Java встречает вызов перегруженного метода, она просто выполняет ту его версию, параметры которой соответствуют аргументам, использованным в вызове. Перегрузка методов поддерживает полиморфизм, поскольку это один из способов реализации в Java концепции “один интерфейс, несколько методов”. Перегрузка ценна тем, что она позволяет обращаться к схожим методам по общему имени. Программисту нужно помнить только об общем выполняемом действии. Полиморфизм позволяет свести несколько имен к одному. Перегрузка конструкторов Наряду с перегрузкой обычных методов можно также выполнять перегрузку методов конструкторов. Фактически перегруженные конструкторы станут нормой, а не исключением, для большинства классов, которые вам придется создавать для реальных программ.
Чтобы это утверждение было понятным, вернемся к классу Box, разработанному в предыдущей главе. Его последняя версия имеет следующий вид: class Box { double width; double height; double depth; // Это конструктор класса Box. Box(double w, double h, double d) { width = w; height = h; depth = d; } // вычисление и возврат значения double volume() { return width * height * depth; } } Как видите, конструктор Box() требует передачи трех параметров. Это означает, что все объявления объектов Box должны передавать конструктору Box() три аргумента. Например, следующий оператор недопустим: Box ob = new Box(); Поскольку конструктор Box() требует передачи трех аргументов, его вызов без аргументов – ошибка. Эта ситуация порождает три важных вопроса. Что если нужно было просто определить параллелепипед и его начальные размеры не имели значения (или не были известны)? Или, нужно иметь возможность инициализировать куб, указывая только один размер, который должен использоваться для всех трех измерений? При текущем определении класса Box все эти дополнительные возможности недоступны. К счастью, решение подобных проблем достаточно просто: достаточно перегрузить конструктор Box, чтобы он учитывал только что описанные ситуации. Ниже приведена программа, которая содержит усовершенствованную версию класса Box, выполняющую эту задачу. /* В этом примере класс Box определяет три конструктора для инициализации размеров параллелепипеда различными способами. */ class Box { double width; double height; double depth; // конструктор, используемый при указании всех измерений Box(double w, double h, double d) { width = w; height = h; depth = d; } // конструктор, используемый, когда ни один из размеров не указан Box() { width = -1; // значение -1 используется для указания height = -1; // неинициализированного depth = -1; // параллелепипеда } // конструктор, используемый при создании куба Box(double len) { width = height = depth = len;
} // вычисление и возврат объема double volume() { return width * height * depth; } } class OverloadCons { public static void main(String args[]) { // создание параллелепипедов с применением различных конструкторов Box mybox1 = new Box(10, 20, 15); Box mybox2 = new Box(); Box mycube = new Box(7); double vol; // получение объема первого параллелепипеда vol = mybox1. volume(); System. out. println(" Объем mybox1 равен " + vol); // получение объема второго параллелепипеда vol = mybox2. volume(); System. out. println(" Объем mybox2 равен " + vol); // получение объема куба vol = mycube. volume(); System. out. println(" Объем mycube равен " + vol); } } Эта программа создает следующий вывод: Объем mybox1 равен 3000. 0 Объем mybox2 равен -1. 0 Объем mycube равен 343. 0 Как видите, соответствующий перегруженный конструктор вызывается в зависимости от параметров, указанных при выполнении операции new. Использование объектов в качестве параметров До сих пор в качестве параметров методов мы использовали только простые типы. Однако передача методам объектов – и вполне допустима, и достаточно распространена. Например, рассмотрим следующую короткую программу: // Методам можно передавать объекты. class Test { int a, b; Test(int i, int j) { a = i; b = j; } // возврат значения true, если параметр o равен вызывающему объекту boolean equals(Test o) { if(o. a == a & & o. b == b) return true; else return false; } } class PassOb { public static void main(String args[]) { Test ob1 = new Test(100, 22); Test ob2 = new Test(100, 22); Test ob3 = new Test(-1, -1); System. out. println(" ob1 == ob2: " + ob1. equals(ob2)); System. out. println(" ob1 == ob3: " + ob1. equals(ob3)); } } Эта программа создает следующий вывод: ob1 == ob2: true ob1 == ob3: false Как видите, метод equals() внутри метода Test проверяет равенство двух объектов и возвращает результат этой проверки. То есть он сравнивает вызывающий объект с тем, который был ему передан. Если они содержат одинаковые значения, метод возвращает значение true. В противном случае он возвращает значение false. Обратите внимание, что параметр o в методе equals() указывает Test в качестве типа. Хотя Test – тип класса, созданный программой, он используется совершенно так же, как встроенные типы Java. Одно из наиболее часто встречающихся применений объектов-параметров – в конструкторах. Часто приходится создавать новый объект так, чтобы вначале он не отличался от какого-то существующего объекта. Для этого потребуется определить конструктор, который в качестве параметра принимает объект его класса. Например, следующая версия класса Box позволяет выполнять инициализацию одного объекта другим:
// В этой версии Box допускает инициализацию одного объекта другим. class Box { double width; double height; double depth; // Обратите внимание на этот конструктор. Он использует объект типа Box. Box(Box ob) { // передача объекта конструктору width = ob. width; height = ob. height; depth = ob. depth; } // конструктор, используемый при указании всех измерений Box(double w, double h, double d) { width = w; height = h; depth = d; } // конструктор, используемый, если ни одно из изменений не указано Box() { width = -1; // значение -1 используется для указания height = -1; // не инициализированного depth = -1; // параллелепипеда } // конструктор, используемый при создании куба Box(double len) { width = height = depth = len; } // вычисление и возврат объема double volume() { return width * height * depth; } } class OverloadCons2 { public static void main(String args[]) { // создание параллелепипедов с применением различных конструкторов Box mybox1 = new Box(10, 20, 15); Box mybox2 = new Box(); Box mycube = new Box(7); Box myclone = new Box(mybox1); // создание копии объекта mybox1 double vol; // получение объема первого параллелепипеда vol = mybox1. volume(); System. out. println(" Объем mybox1 равен " + vol); // получение объема второго параллелепипеда vol = mybox2. volume(); System. out. println(" Объем mybox2 равен " + vol); // получение объема куба vol = mycube. volume(); System. out. println(" Объем куба равен " + vol); // получение объема клона vol = myclone. volume(); System. out. println(" Объем клона равен " + vol); } } Как вы убедитесь, приступив к созданию собственных классов, чтобы объекты можно было конструировать удобным и эффективным образом, нужно располагать множеством форм конструкторов. В общем случае существует два способа, которыми компьютерный язык может передавать аргументы подпрограмме. Первый способ – вызов по значению. При использовании этого подхода значение аргумента копируется в формальный параметр подпрограммы. Следовательно, изменения, выполненные в параметре подпрограммы, не влияют на аргумент. Второй способ передачи аргумента – вызов по ссылке. При использовании этого подхода параметру передается ссылка на аргумент (а не его значение). Внутри подпрограммы эта ссылка используется для обращения к реальному аргументу, указанному в вызове. Это означает, что изменения, выполненные в параметре, будут влиять на аргумент, использованный в вызове подпрограммы. Как вы убедитесь, в Java применяются оба подхода, в зависимости от передаваемых данных.
В Java элементарный тип передается методу по значению. Таким образом, все происходящее с параметром, который принимает аргумент, не оказывает влияния вне метода. При передаче объекта методу ситуация изменяется коренным образом, поскольку по существу объекты передаются посредством вызова по ссылке. Следует помнить, что при создании переменной типа класса создается лишь ссылка на объект. Таким образом, при передаче этой ссылки методу, принимающий ее параметр будет ссылаться на тот же объект, на который ссылается аргумент. По сути это означает, что объекты передаются методам посредством вызова по ссылке. Изменения объекта внутри метода влияют на объект, использованный в качестве аргумента. Например, рассмотрим такую программу: // Объекты передаются по ссылке. class Test { int a, b; Test(int i, int j) { a = i; b = j; } // передача объекта void meth(Test o) { o. a *= 2; o. b /= 2; } } class CallByRef { public static void main(String args[]) { Test ob = new Test(15, 20); System. out. println(" ob. a и ob. b перед вызовом: " + ob. a + " " + ob. b); ob. meth(ob); System. out. println(" ob. a и ob. b после вызова: " + ob. a + " " + ob. b); } } Эта программа генерирует следующий вывод: ob. a и ob. b перед вызовом: 15 20 ob. a и ob. b после вызова: 30 10 Как видите, в данном случае действия внутри метода meth() влияют на объект, использованный в качестве аргумента. Интересно отметить, что когда ссылка на объект передается методу, сама ссылка передается посредством вызова по значению. Однако поскольку передаваемое значение ссылается на объект, копия этого значения все равно будет ссылаться на тот же объект, что и соответствующий аргумент. Когда элементарный тип передается методу, это выполняется посредством вызова по значению. Объекты передаются неявно с помощью вызова по ссылке.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|