
Cобеседования в IT - это всегда вызов и Android тут, конечно же, не исключение. Для успешного прохождения интервью в крупные и востребованные компании современный Android-разработик должен держать в голове огромное количетсво областей: от "базы" в виде Computer Science (структуры данных, алгоритмы) и навыков проектирования (архитектура, паттерны, System Design) до знания Java, Kotlin и, разумеется Android SDK. Добавим сюда и популярные фреймворки и библиотеки (которые к тому же устаревают каждые пару лет!) вроде RxJava и Kotlin Coroutines и получается совсем уж, на первый взгляд, неподъемный багаж необходимых знаний... К счастью, многие вопросы пересекаются между компаниями, так что наработав некоторую "базу" из них вы сильно повысите свои шансы на положительный результат и заветный оффер :) Давайте разберем ТОП-5 вопросов, с которыми можно столкнуться в крупных компаниях на российском рынке, поехали!
1. Перечислите принципы, входящие в SOLID.
Вопрос вне времен и трендов: годы идут, а значительная часть интервью не обходится без этой темы. Чаще всего, все ограничивается перечислением и принципов и их коротким описанием, потому кратко давайте разберем их.
Single Responsibility: часто неправильно понимается, как "модуль или класс должны отвечать за что-то одно". Это отчасти верно, но речь идет не только об ответственности класса, но еще и о причинах для его изменения, точнее о группе лиц, которым может потребоваться его изменить: "модуль должен отвечать только за одного актора" (где актор - 1 или более лиц, объединенных группой, например - "бухгалтерия", "отдел кадров").
Open-Closed: сущности программы должны быть открыты для расширения и закрыты для из изменений. Простое расширение (а не изменение) требований не должно вести к значительным изменениям кода, иначе - это архитектурная ошибка, произошедшая по причине нарушения этого принципа.
Liskov Substitution: тут речь идёт о правильной иерархии наследования. Везде, где мы можем использовать 1 класс (базовый, например), мы должны иметь возможность использовать и его наследников без неожиданного поведения. Мораль - наследники должны писаться разумно и должны дополнять, а не замещать или искажать поведение базового класса.
Interface Segregation: пожалуй, самый простой и понятный из принципов, где не часто бывают различные интерпретации. Принцип говорит о том, что несколько узко специализированных интерфейсов лучше, чем один большой. Один большой интерфейс может заставить классы, которые его реализуют, имплементировать методы, которые могут быть им и не нужны в конкретных случаях, что приводит к лишней работе или засорению кода пустыми реализациями.
Dependency Inversion: Принцип гласит, что модули верхнего уровня не должны зависеть от модулей нижнего, и те и другие должны зависеть от абстракций И абстракции не должны зависеть от деталей, детали должны зависеть от абстракций. На практике это выглядит так: классы не должны создавать другие классы внутри себя, особенно конкретные реализации. Эти зависимости должны поставляться ИЗВНЕ, то есть инвертироваться.
Будьте готовы к усложнениям и вариациям, например:
Какой из данных принципов на ваш взгляд наиболее важный?
Какие нарушения принципов SOLID вы можете вспомнить в стандартных классах из Java, Kotlin или Android SDK?
Что почитать:
Роберт Мартин (Uncle Bob) - Чистая архитектура - собственно, это автор, который сформулировал принципы, входящие в SOLID, так что это информация напрямую от первоисточника
Baeldung (c примерами на Java)
2. Как работает HashMap? Как происходит разбиение на бакеты? Что такое коллизии? Какую роль играют реализации equals/hashCode методов у добавляемых объектов?
HashMap - излюбленная коллекция у многих собеседующих. Тема эта достаточно обширная, но, к счастью, материалов на этот счёт написано огромное количество:
можно посмотреть статьи в блогах: например, прямо тут на Хабре
литература: классикой для Java считается книга Роберта Лафоре - Структуры и алгоритмы данных Java
3. Что такое data классы в Kotlin? Зачем они нужны, чем отличаются от обычных?
Data classes - это по праву одна из наиболее любимых многими фич Котлина! Собеседующие, правда, тоже попадают в эту категорию, потому они очень и очень любят задавать о ней вопросы. Начать знакомство с ними стоит в документации, чтобы понять базовые идеи.
Но стоит быть готовым и к усложнениям:
Чем
dataкласс отличается отvalueкласса? (это довольно разные концепты, см. документацию для value класса)Что такое
data object?В чем разницу междуdata classиdata object? (см. документацию)Какую информацию из data класса компилятор использует для генерации
equals/hashCode/toString? (поля конструктора, переменные вне конструктора не используются)Что будет, если объявить поля конструктора
dataкласса какvar, а неval? (ничего криминального, но это нарушает идею data классов - класс перестает быть неизменяемым, наличие сгенерированной функции copy() начинает выглядеть странно и т.д.)Есть ли у
dataклассов какие-то недостатки? Нельзя ли просто все классы объявлять именно какdata? (есть, например, они влияют на размер приложения из-за кодогенерации)
4. Какие "основные" компоненты Андроид вы знаете и почему они так называются?
Под “основными” понимаются те компоненты, которые могут являться точкой входа в приложение. Теоретически, приложение может состоять только из одного из таких компонентов, зарегистрированного в AndroidManifest, в этом и заключается их особенность. Согласно документации, всего их 4:
Т.е. да, вы можете создать приложение, в котором нет никакого привычного UI и которое состоит из одного лишь BroadcastReceiver или Service и, возможно, даже выложить его в Google Play.
5. Как создать новую корутину? Перечислите все способы, которые знаете.
Основной способ запуска корутин - это использование т.н. Coroutine Builders:
launch: запускает новую корутину и возвращает связанный с ней Job. Этот
Jobне предоставляет типизированного результата, а просто служит индикатором выполнения + позволяет отменить выполняемую работу. Подходит, когда не нужен или не важен результат выполнения корутины, нужно просто запустить ее и выполнить.async: в отличии от
launchвозвращает неJob, а Deferred<T>. Это future/promise, который по умолчанию уже находится в запущенном состоянии (это можно контролировать через параметрstart). Чтобы дождаться результата выполнения необходимо вызватьawaitна возвращенномDeferredобъекте. Этот билдер удобен, когда необходимо получить результат выполнения работы корутины, а не простоJobизlaunch.runBlocking: запускает новую корутину и блокирует вызывавший поток до ее завершения. Возвращается не
JobилиDefferred, а обычный типизированный типT. Этот способ не является основным, т.к. он сводит преимуществаsuspendingфункций на "нет", но он бывает полезен в случаях, когда нужно вызывать корутины из какого-то старого кода, библиотек без поддержки kotlin и/или корутин + иногда используется при написании тестов.
launch, async являются extension функциями над CoroutineScope, т.е. их нельзя вызвать вне контекста корутин (за счет этого наследуется context и прокидывается cancellation). runBlocking же доступен отовсюду и не является extension функцией.
Все упомянутые билдеры получают CoroutineScope.() лямбду на вход, внутри которой уже можно вызывать другие билдеры и suspend функции.
На этом всё! Удачи на собеседованиях :-)
А если вам интересн такой формат подготовки и вы хотели бы серьезно поднять свои шансы на заветный оффер, то рекомендую попробовать курс "Android собеседования от А до Я: популярные вопросы и ответы к ним". Курс содержит разбор более 350-и популярных технических вопросов, которые встречаются на реальных Android собеседованиях в крупные компании в РФ — Ozon, Tinkoff, Альфабанк, Сбер и др.
egl85
А нормальных вопросов уже не задают?