Java: Пакеты

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

При таком количестве классов почти неизбежно возникает ситуация, когда два разных класса получают одинаковые имена. Если два класса с одинаковым именем оказываются в одном проекте, программа не скомпилируется. Свой класс переименовать можно, а вот с классом из чужой библиотеки так не получится. Поэтому требование уникальности имен сильно мешало бы переиспользовать чужой код.

Чтобы решить эту проблему, в Java используют пакеты. Пакет — это механизм для объединения классов в логически связанные группы. Пакеты похожи на папки в файловой системе: как папки организуют файлы, так пакеты организуют классы. Разные пакеты могут содержать классы с одинаковыми именами, и конфликта не возникнет.

Определение пакетов

Пакет задают ключевым словом package в самом начале файла, сразу после него идет имя пакета:

// Файл company/User.java
package company;

public class User {
    // код для работы с пользователем
}

Структура пакетов связана с файловой структурой проекта: имя пакета соответствует директории, в которой лежит файл. Пакеты бывают вложенными — тогда и директории вложены друг в друга. Если класс лежит в пакете io.hexlet.model, то и файл находится в директории io/hexlet/model.

Обычно имя пакета начинают с префикса, закрепленного за компанией или разработчиком, — чаще всего это доменное имя в обратном порядке. Например, для домена hexlet.io пакеты начинаются с io.hexlet. Дальше структура зависит от архитектуры приложения: классы группируют по смыслу, например в пакет model складывают основные сущности — пользователей и курсы:

// Файл io/hexlet/model/User.java
package io.hexlet.model;

public class User {
    public static String getGreeting(String userName) {
        return "Hello, " + userName + "!";
    }
}

Импорт классов

Классы из одного пакета обращаются друг к другу просто по имени. Но постоянно приходится использовать и классы из других пакетов. Чтобы обратиться к классу из другого пакета, его нужно импортировать — для этого служит ключевое слово import, после которого идет полное имя класса:

package io.hexlet;

import io.hexlet.model.User;

class App {
    public static void main(String[] args) {
        var greeting = User.getGreeting("John");
        System.out.println(greeting);
    }
}

После импорта к классу обращаются по короткому имени. Без импорта пришлось бы каждый раз писать полное (fully qualified) имя, включая название пакета:

var greeting = io.hexlet.model.User.getGreeting("John");

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

Можно импортировать сразу все классы пакета с помощью *:

import java.util.*;

Так удобно, когда из пакета нужно много классов, но злоупотреблять этим не стоит: импорт «всего подряд» захламляет пространство имен и повышает риск конфликта имен между пакетами.

Статический импорт

Java позволяет импортировать не только классы, но и отдельные статические методы — тогда их можно вызывать без указания класса. Это удобно, когда какой-то метод используется часто, и делает код компактнее:

import static java.lang.Math.ceil;

public class App {
    public static void main(String[] args) {
        // Имя класса Math при вызове можно опустить
        double result = ceil(2.3); // 3.0
        System.out.println(result);
    }
}

Можно импортировать и сразу все статические методы класса: import static java.lang.Math.*;. Статический импорт мы используем в задании этого урока.

Задание

Реализуйте метод amountPerPerson(). Он принимает сумму счета в ресторане total, количество человек people и процент чаевых tipPercent, а возвращает сумму, которую платит каждый. Итог округляется вверх — никто не должен недоплатить.

Для округления вверх используйте метод ceil(). Он уже подключен в начале файла статическим импортом из класса Math, поэтому вызывать его можно без префикса Math..

App.amountPerPerson(300, 4, 20); // => 90
App.amountPerPerson(350, 3, 10); // => 129

Сначала посчитайте итоговую сумму с чаевыми, затем разделите ее на количество человек и округлите вверх. Результат метода ceil() имеет тип double, а вернуть нужно int — не забудьте про приведение типа.

Полезное

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются