Программирование на Java
Шрифт:
Перегруженными (overloaded) методами называются методы одного класса с одинаковыми именами. Сигнатуры у них должны быть различными и различие может быть только в наборе аргументов.
Если в классе параметры перегруженных методов заметно различаются: например, у одного метода один параметр, у другого – два, то для Java это совершенно независимые методы и совпадение их имен может служить только для повышения наглядности работы класса. Каждый вызов, в зависимости от количества параметров, однозначно адресуется тому или иному методу.
Однако если количество параметров одинаковое, а типы их различаются незначительно, при вызове может сложиться двойственная ситуация,
можно сказать, что они допустимы, их сигнатуры различаются. Однако при вызове
process(new Child, new Child);
обнаруживается, что оба метода одинаково годятся для использования. Другой пример, методы:
и примеры вызовов:
Очевидно, что для первых двух вызовов подходит только первый метод, и именно он будет вызван. Для последнего же вызова подходят оба перегруженных метода, однако класс String является более "специфичным", или узким, чем класс Object. Действительно, значения типа String можно передавать в качестве аргументов типа Object, обратное же неверно. Компилятор попытается отыскать наиболее специфичный метод, подходящий для указанных параметров, и вызовет именно его. Поэтому при третьем вызове будет использован второй метод.
Однако для предыдущего примера такой подход не дает однозначного ответа. Оба метода одинаково специфичны для указанного вызова, поэтому возникнет ошибка компиляции. Необходимо, используя явное приведение, указать компилятору, какой метод следует применить:
process((Parent)(new Child), new Child);
// или
process(new Child,(Parent)(new Child));
Это верно и в случае использования значения null:
process((Parent)null, null);
// или
process(null,(Parent)null);
Заключение
В этой лекции началось рассмотрение ключевой конструкции языка Java – объявление класса.
Первая тема посвящена средствам разграничения доступа. Главный вопрос – для чего этот механизм вводится в практически каждом современном языке высокого уровня. Необходимо понимать, что он предназначен не для обеспечения "безопасности" или "защиты" объекта от неких неправильных действий. Самая важная задача – разделить внешний интерфейс класса и детали его реализации с тем, чтобы в дальнейшем воспользоваться такими преимуществами ООП, как инкапсуляция и модульность.
Затем были рассмотрены все четыре модификатора доступа, а также возможность их применения для различных элементов языка. Проверка уровня доступа выполняется уже во время компиляции и запрещает лишь явное использование типов. Например, с ними все же можно работать через их более открытых наследников.
Объявление класса состоит
Кроме того, в теле класса объявляются конструкторы и инициализаторы. Поскольку они не являются элементами, к ним нельзя обратиться явно, они вызываются самой виртуальной машиной. Также конструкторы и инициализаторы не передаются по наследству.
Дополнительно был рассмотрен метод main, который вызывается при старте виртуальной машины. Далее описываются тонкости, возникающие при передаче параметров, и связанный с этим вопрос о перегруженных методах.
Классы Java мы продолжим рассматривать в следующих лекциях.
7. Лекция: Преобразование типов
Эта лекция посвящена вопросам преобразования типов. Поскольку Java – язык строго типизированный, компилятор и виртуальная машина всегда следят за работой с типами, гарантируя надежность выполнения программы. Однако во многих случаях то или иное преобразование необходимо осуществить для реализации логики программы. С другой стороны, некоторые безопасные переходы между типами Java позволяет осуществлять неявным для разработчика образом, что может привести к неверному пониманию работы программы. В лекции рассматриваются все виды преобразований, а затем все ситуации в программе, где они могут применяться. В заключение приводится начало классификации типов переменных и типов значений, которые они могут хранить. Этот вопрос будет подробнее рассматриваться в следующих лекциях.
Введение
Как уже говорилось, Java является строго типизированным языком, а это означает, что каждое выражение и каждая переменная имеет строго определенный тип уже на момент компиляции. Тип устанавливается на основе структуры применяемых выражений и типов литералов, переменных и методов, используемых в этих выражениях.
Например:
long a=3;
a = 5+'A'+a;
print("a="+Math.round(a/2F));
Рассмотрим, как в этом примере компилятор устанавливает тип каждого выражения и какие преобразования (conversion) типов необходимо осуществить при каждом действии.
* В первой строке литерал 3 имеет тип по умолчанию, то есть int. При присвоении этого значения переменной типа long необходимо провести преобразование.
* Во второй строке сначала производится сложение значений типа int и char. Второй аргумент будет преобразован так, чтобы операция проводилась с точностью в 32 бита. Второй оператор сложения опять потребует преобразования, так как наличие переменной a увеличивает точность до 64 бит.
* В третьей строке сначала будет выполнена операция деления, для чего значение long надо будет привести к типу float, так как второй операнд - дробный литерал. Результат будет передан в метод Math.round, который произведет математическое округление и вернет целочисленный результат типа int. Это значение необходимо преобразовать в текст, чтобы осуществить дальнейшую конкатенацию строк. Как будет показано ниже, эта операция проводится в два этапа - сначала простой тип приводится к объектному классу-"обертке" (в данном случае int к Integer ), а затем у полученного объекта вызывается метод toString, что дает преобразование к строке.