Как вызывать методы в java

Руководство по Java Core. Методы.

Метод – это набор выражений, которые объединены вместе для выполнения определённой операции.

Например, когда мы вызываем метод System.out.println(), начинается выполнение целого ряда операций для того, чтобы в итоге отобразить вывести сообщение в консоль.

В этом уроке мы более подробно изучим, как создавать собственные методы. Рассмотрим методы, возвращающие определённое значение, и которые – нет. Также, мы поговорим о том, что такое перезагрузка метода.

В общем виде, методы выглядит следующим образом:

Метод состоит из следующих сущностей:

  • Модификатор
    Модификатор метода говорит компилятору как вызывать данный метод и определяет уровень доступа данного метода. Модификатор не является обязательной частью объявления метода.
  • Возвращаемый тип
    Метод возвращать значение. Возвращаемый тип – это тип данных, который будет возвращён данным методом. Если нам не нужно, чтобы метод возвращал значение, то мы должным использовать ключевое слово void в качестве возвращаемого типа.
  • Имя метода
    Непосредственно, имя метода, которе является его идентификатором.
  • Параметры
    Если нам необходимо, выполнить определённые вычисления, например получить сумму двух чисел, то мы должны передать методу эти два числа. Передать эти числа мы можем с помощью параметров метода. Мы должны указать тип данных этих параметров. Метод также может не иметь параметров.
  • Тело метода
    Тело метода включает в себя набор выражений, которые определяют поведение метода. В случае со сложением двух чисел, метод выполнит операцию сложения.

Для понимания того, как это работает на практике, рассмотрим пример простого приложения.

В результате работы программы мы получим следующий результат:

Как мы видим, первый метод принимает два параметра и возвращает значение (сумму этих чисел). Второй метода, также принимает два параметра, но не возвращает никакого значения. Вместо этого он определяет максимальное число и выводит текстовое сообщение.

Перегрузка методов

Иногда нам необходимо выполнять одни и те же операции для разных типов данных. Например, нам необходимо определять сумму не только для целых чисел, но и для чисел с плавающей точкой. Перегруженный метод должен иметь такое же название, как и первый, но список параметров должен отличаться (либо типы данных, либо количество параметров, либо и то, и другое). Рассмотрим пример простого приложения.

В результате работы программы мы получим следующий результат:

Конструкторы

Конструктор по своей сути тоже метод, имеющий такое же название, как и класс, в котором он создан. Его задача создавать экземпляры данного классы (объекты).

Все классы имеют конструктор. Даже если он не объявлен явно, то используется конструктор по умолчанию (без аргументов – всем переменным присваиваются значения по умолчанию, согласно спецификации).

Рассмотрим пример простого приложения с использованием конструктора.

В результате работы программы мы получим следующий результат:

В этом уроке мы более подробно изучили, что такое методы, какие виды методов существуют и рассмотрели примеры с их использованием.

Удобоваримый вызов Java методов из нативного кода

Существует довольно много приложений под Android, которые совмещают C++ и Java код. Где Java выступает оберткой/прослойкой, а C++ выполняет всю грязную работу. Пожалуй, ярким примером могут служить игры. В связи с этим часто приходится вызывать Java код из нативного для доступа к системным свойствам и плюшкам, которые предоставляет система (переключится на другую активность, послать или скачать что-либо из интернета). Причин много, а проблема одна: каждый раз приходится писать в лучшем случае 5 строчек кода и помнить, какую сигнатуру функции нужно запихнуть в параметр. Потом еще нужно перевести эти параметры в нужный тип. Стандартный пример из туториалов:

Строка-сигнатура для данного метода будет (ILjava/lang/String;F)J.

Вам удобно это все запоминать? А переводить С-строки в jstring? Мне — нет. Мне хочется писать:

Постановка задачи

Для начала поймем, что нам нужно. В сущности, это четыре вещи:

  1. Вызвать метод;
  2. Из параметров нужно вытянуть строку сигнатуры. Да, да, вот эту (ILjava/lang/String;F)J;
  3. Сконвертировать параметры в нужный тип;
  4. Возвратить тип данных, который хочет видеть пользователь нашего класса.

Вызов метода

Теперь стоит отметить, как мы будем вызывать нашу функцию-оболочку. Так как параметров может разное количество (от нуля и больше), то нужна функция вроде print`а в стандартной библиотеке, но с тем, чтобы было удобно вытягивать тип параметра и сам параметр. В С++11 появились вариадические шаблоны. Ими и воспользуемся.

Составляем сигнатуру

    Используем typeid и цепочку if … else. Должно получится что-то вроде:

Вызов всего этого непотребства:

Рекурсия — это хорошо в воспитательно-образовательных целях, но предпочитаю ее обходить при возможности. Тут такая возможность есть. Так как аргументы идут последовательно и мы можем узнать количество аргументов можно использовать удобство предоставленное стандартом С++11. Код преобразуется в:

Кода вроде бы и больше, но работает оно быстрее. Хотя бы за счет того, что не вызываем функций больше, чем нам это нужно.

Конвертация типа данных

После пыток попыток и ухищрений было решено использовать CallStaticMethodA(JNIEnv*, jclass, jmethodID, jvalue*). Теперь только нужно привести все параметры к jvalue. Сам jvalue это union, в котором нужно установить нужное поле в зависимости от типа данных, которые вам передали любимые пользователи. Мудрить не будем и создаем структуру (или класс; дело вкуса) JniHolder с конструкторами нужных типов.

Где JObjectHolder — обертка для удержания и удаления jobject`а.

Создается объект JniHolder, куда передаются JNIEnv* и значение. В конструкторе мы знаем какое поле нужно выставить в jvalue. Чтобы не было соблазна у компилятора приводить типы незаметно, все конструкторы делаем explicit. Вся цепочка занимает одну строчку:

Но есть одно но. Когда преобразования происходит мы возвращаем jvalue, но у нас удаляется jObject и val.l указывает на невалидный адрес. Поэтому приходится сохранять холдеры во время вызова функции java.

В случае передачи нескольких параметров используем список инициализации:

Возвращение нужного типа данных

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

Но есть неприятная особенность JNI: для каждого возвращаемого типа есть свой конкретный метод. То есть, для int вам нужен CallStaticIntMethod, для float – CallStaticFloatMethod и так далее. Пришел к частичным типизациям шаблонов. Сначала объявляем нужный нам интерфейс:

Потом для каждого типа пишем реализацию. Для целых чисел (int) будет выглядеть:

Если у нас ноль параметров, то нужно вызывать CallStaticMethod, а не CallStaticMetodA. Ну и если пытаться создать массив размерностью ноль, компилятор сообщит вам все, что думает по этому поводу.

Финал

Сам метод вызова выглядит:

Теперь вызов метода из java:

Где-то в нативном коде:

Выводы

Вызовы методов превратились в удобную вещь и не надобно запоминать сигнатуры и конвертировать значения и удалять ссылки. Достаточно передавать название класса, метода и аргументы.

Также получилась интересная задачка, благодаря которой немного поразбирался с новыми плюшками языка (которые мне ну очень понравились) и вспомнил шаблоны.

Благодарю за прочтение. Ну или за внимание, если вы не все прочитали. С радостью прочитаю предложения по улучшению и критику работы. А также отвечу на вопросы.

Изучаем Java

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

Рассмотрим следующие модификаторы.

• Модификаторы доступа — они уже подробно разбирались.
• Модификаторы strictpf, static — они также уже рассматривались.
• Abstract. Он предусматривает только объявление метода. Метод должен быть определен в классах-наследниках (подробнее об этом — в разделе "Наследование").
• Final. Методы не могут переопределяться в классах-наследниках (об этом подробнее см. в том же разделе).
• Native. Если у метода есть такой модификатор, то данный метод написан на другом языке программирования (например, на языке С++).
• Sinchronized— этот модификатор означает, что данный метод защищен от разрушения данных при попытке использования этих данных несколькими методами сразу.

Создадим теперь простой метод и вызовем его (листинг 4.5).

Листинг 4.5.
Создание и вызов простого метода

У каждого метода в скобках можно помещать аргументы, или параметры. С помощью аргументов можно передать какие-либо значения методу, над которыми он будет работать. Если передавать методу ссылку (на объект, переменную и т.д.), то значение каких- либо свойств объекта могут измениться. Приведем пример использования методов с аргументами (листинг 4.6).

Листинг 4.6.
Пример использования методов с аргументами

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

Ранее мы использовали параметр, являющийся массивом объектов String с именем args, который указывается в методе main. Если запускать программу из командной строки, то можно передать в программу несколько значений, которые последовательно будут записываться в элементы этого массива. Если это необходимо, можно получить к ним доступ, указывая соответствующие элементы массива.

Возможно также наличие следующего модификатора типа возвращаемого значения (мы уже указывали модификатор void, означающий то, что метод ничего не возвращает). Метод может возвращать какое-то одно значение. Чтобы метод возвращал значение, нужно указать модификатор типа значения, которое вы собираетесь возвращать, а в конце выполнения метода добавить return с переменной (или значением).

В листинге 4.7 представлен пример использования метода, имеющего модификатор типа возвращаемого значения.

Листинг 4.7.
Пример использования метода, имеющего модификатор типа j возвращаемого значения

Перегруженные методы

Если два и более методов класса имеют одно имя, но их параметры не совпадают, то такие методы называют перегруженными. Примером перегруженных методов являются конструкторы. Пример в листинге 4.8.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *