Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Для чего нужен package private?
Package private (отсутствие модификатора доступа, или default access) — это модификатор видимости, который ограничивает доступ к членам класса только классами в одном пакете.
Что такое package private
Это когда ты НЕ пишешь никакой модификатор (ни public, ни private, ни protected):
// Package private класс
class UserRepository { // НЕ public!
void save(User user) { }
}
// Package private метод
public class UserService {
String getPassword(User user) { // НЕ public!
return user.password;
}
}
Модификаторы доступа в Java
| Модификатор | Тот же класс | Тот же пакет | Подкласс (другой пакет) | Все |
|---|---|---|---|---|
| private | ✓ | ✗ | ✗ | ✗ |
| package-private (default) | ✓ | ✓ | ✗ | ✗ |
| protected | ✓ | ✓ | ✓ | ✗ |
| public | ✓ | ✓ | ✓ | ✓ |
Пример структуры проекта
com.example.users/
├── UserService.java (public класс)
└── UserRepository.java (package-private класс)
com.example.orders/
├── OrderService.java
└── OrderRepository.java
Видимость между пакетами
// В пакете com.example.users
public class UserService {
public User getUser(int id) { }
}
class UserRepository { // Package-private!
void save(User user) { }
}
// В пакете com.example.orders
public class OrderService {
public void createOrder(int userId) {
UserService service = new UserService(); // OK (public класс)
service.getUser(userId); // OK (public метод)
UserRepository repo = new UserRepository(); // ОШИБКА!
// Не видна за пределами пакета com.example.users
}
}
Для чего нужен package-private
1. Скрыть внутренние детали реализации
// Пакет com.example.auth
// Public API
public class AuthService {
public boolean validateToken(String token) {
return TokenValidator.validate(token);
}
}
// Внутренний вспомогательный класс
class TokenValidator { // Package-private!
static boolean validate(String token) {
// Сложная логика валидации
}
}
// Внешний код может использовать AuthService,
// но НЕ может напрямую вызвать TokenValidator
2. Контроль над архитектурой пакета
// Правильная архитектура:
// UserService (PUBLIC) — фасад
// ├─ UserRepository (package-private) — данные
// ├─ UserValidator (package-private) — валидация
// └─ UserMapper (package-private) — преобразование
public package com.example.users;
// API
public class UserService {
private UserRepository repository;
private UserValidator validator;
private UserMapper mapper;
public User createUser(CreateUserRequest request) {
validator.validate(request); // Используем внутренний класс
User user = mapper.toEntity(request);
repository.save(user); // Используем внутренний класс
return user;
}
}
// Внутренние детали
class UserRepository { }
class UserValidator { }
class UserMapper { }
// Снаружи пакета видна только UserService!
3. Предотвращение неправильного использования
// Пакет com.example.database
// Клиенты должны использовать сервисы, не Connection напрямую
public class DatabaseService {
public User getUser(int id) {
Connection conn = getConnection(); // Package-private метод
// ...
}
}
// Это скрыто и недоступно извне пакета
class DatabaseConnectionPool { }
class DatabaseConfig { }
Реальный пример: Spring Framework
// В org.springframework.context
// PUBLIC API
public class ClassPathXmlApplicationContext implements ApplicationContext {
public ClassPathXmlApplicationContext(String configLocation) {
// ...
}
}
// ВНУТРЕННИЕ детали (package-private)
class XmlBeanDefinitionReader { }
class BeanDefinitionParser { }
class ResourceLoader { }
// Клиенты используют ClassPathXmlApplicationContext,
// но не видят внутренней реализации
Лучшие практики
1. Минимизируй public, максимизируй package-private
// Плохо: слишком много public
public class User {
public String name; // public
public void setName(String n) { } // public
public void validateName() { } // public
public boolean isNameValid() { } // public
}
// Хорошо: скрываем детали
public class User {
private String name; // private
public void setName(String n) { // public (нужно снаружи)
if (isNameValid(n)) {
this.name = n;
}
}
private boolean isNameValid(String n) { } // package-private (вспомогательный)
}
2. Используй package-private для helper классов
// Пакет com.example.payment
// Main API
public class PaymentService {
public PaymentResult process(PaymentRequest request) {
validator.validate(request);
processor.process(request);
}
}
// Helper классы
class PaymentValidator { // Package-private
void validate(PaymentRequest request) { }
}
class PaymentProcessor { // Package-private
void process(PaymentRequest request) { }
}
3. Package-private для тестирования
// Production код
public class UserService {
private UserRepository repository;
// Package-private для тестирования
UserRepository getRepository() {
return repository;
}
}
// Тесты в том же пакете
public class UserServiceTest {
void testRepository() {
UserService service = new UserService();
UserRepository repo = service.getRepository(); // OK!
}
}
Иерархия видимости (от самого видимого к самому скрытому)
public
↓
protected (только для наследников)
↓
package-private (только в пакете)
↓
private (только в классе)
Когда используется в реальных проектах
✓ Spring Boot — много package-private компонентов ✓ Google Guava — скрывает внутреннюю реализацию ✓ Apache Commons — использует package-private для helper классов ✓ JDK — java.lang.* содержит множество package-private классов
Вывод
Package-private нужен для:
-
Скрытия внутренней реализации — клиенты видят только публичный API
-
Контроля архитектуры — предотвращаешь неправильное использование классов
-
Упрощения рефакторинга — можешь менять внутренние классы без нарушения API
-
Предотвращения dependency chains — клиенты не зависят от внутренних деталей
-
Улучшения безопасности — скрываешь конфиденциальные детали реализации
Правило 80/20: В хорошем проекте ~20% классов public, остальные 80% package-private или private.