Отделение «описания методов» от их реализации. Раньше я тебе рассказывал, что если ты хочешь разрешить вызывать методы своего класса из других классов, то их нужно пометить ключевым словом public. Если же хочешь, чтобы какие-то методы можно было вызывать только из твоего же класса, их нужно помечать ключевым словом private. Другими словами мы делим методы класса на две категории: «для всех» и «только для своих».
С помощью интерфейсов, это деление можно усилить еще больше. Мы сделаем специальный «класс для всех», и второй «класс для своих», который унаследуем от первого. Вот как это примерно будет:
Пример 1
class Student {
private String name;
public Student(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
private void setName(String name) {
this.name = name;
}
Класс Main
public static void main(String[] args) {
Student student = new Student("Alibaba");
System.out.println(student.getName());
}
Пример 2
interface Student {
public String getName();
}
class StudentImpl implements Student {
private String name;
public StudentImpl(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
private void setName(String name) {
this.name = name;
}
}
Мы разбили наш класс на два: интерфейс и класс, унаследованный от интерфейса.
— И в чем тут преимущество?
— Один и тот же интерфейс могут реализовывать (наследовать) различные классы. И у каждого может быть свое собственное поведение. Так же как ArrayList и LinkedList – это две различные реализации интерфейса List.
Таким образом, мы скрываем не только различные реализации, но и даже сам класс, который ее содержит (везде в коде может фигурировать только интерфейс). Это позволяет очень гибко, прямо в процессе исполнения программы, подменять одни объекты на другие, меняя поведение объекта скрытно от всех классов, которые его используют.
Это очень мощная технология в сочетании с полиморфизмом. Сейчас далеко не очевидно, зачем так нужно делать. Ты должен сначала столкнуться с программами, состоящими из десятков или сотен классов, чтобы понять, что интерфейсы способны существенно упростить тебе жизнь.
Не очень пойму зачем мы делаем Сеттер private, чтобы просто могли получить данные благодаря Геттеру?
На мой взгляд, данный пример интерфейса и его реализации не очень удачный. От этого и появляется вопрос: "А зачем, собственно, использовать интерфейс?". Для себя я понял так: Интерфейс нужен тогда, когда предусматривается несколько реализаций. Для наглядности рассмотрим класс StudentService
, у которого будут методы getAll()
и getByName()
(думаю названия методов говорят сами за себя).
public class StudentService {
public List<Student> getAll() {
// ...
}
public Student getByName(String name) {
// ...
}
}
Очевидно, что может существовать не одна реализация данного интерфейса. Студенты могут "браться" из базы данных, из файла, в конце концов, может быть просто статический массив в самом классе. Также, для наглядности, давайте введем класс StudentController
задача которого, отобразить информацию о студенте/студентах. Это может быть как андроид приложение, так и десктоп, это не важно, важно что класс StudentController
будет использовать StudentService
.
public class StudentController {
private StudentService studentService;
public StudentController(StudentService ss) {
this.studentService = ss;
}
public void showStudentInfo() {
String name = ...; // получаем имя студента
Student student = this.studentService.getByName(name); // находим студента по имени
// отображаем информацию
}
}
Сейчас поле private StudentService studentService
- это конкретный класс (в котором информация берется, допустим из статического массива). Теперь, допустим, я хочу добавить сервис для поиска студентов в базе данных и использовать его в StudentController
. Что мне нужно для этого сделать? Во-первых, очевидно, создать сам класс (допустим DatabaseStudentService
), который будет взаимодействовать с базой данных. Во-вторых, в StudentController
нужно изменить тип из StudentService
на DatabaseStudentService
. И здесь уже можно заметить следующую проблему: каждый раз когда Вы захотите изменить StudentService
нужно изменить StudentController
. Чтобы этого избежать, нужно ввести интерфейс StudentService
(пока это был конкретный класс) и реализации:
public interface StudentService {
public List<Student> getAll();
public Student getByName(String name);
}
public class StaticArrayStudentService implements StudentService {
// этот класс содержит конкретную реализацию
}
public class DatabaseStudentService implements StudentService {
// этот класс содержит конкретную реализацию
}
Ко всему этому я использовал прием под названием Dependency Injection. То есть, экземпляр класса создаю не в самом классе StudentController
, а "за его пределами" (передавая StudentService
в качестве параметра конструктора):
StudentService ss = new DatabaseStudentService();
StudentController sc = new StudentController(ss);
Теперь для того, чтобы поменять реализацию StudentService
в StudentController
нужно изменить первую строку в коде выше (класс StudentController
остается без изменений).
Зачем у класса Student
приватный сеттер? Вероятно для того, чтобы нельзя было поменять имя студента (действительно, в реальной жизни имя у человека не меняется, ну если человек законопослушный ;) ).
Многовато получилось, надеюсь мой ответ Вам немного поможет разобраться!
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Всем привет! Необходимо скачать файл по имеющейся ссылке и сохранить по указанному путиПуть указать несложно, скачать файл, относительно,...
Прочитал статью на Хабре и вот мои примеры и информация оттуда: