- Классы и объекты
- Классы
- Объекты
- Ключевое слово final
- Ключевое слово instanceof — Проверка принадлежности к классу
- import — Импорт класса
- Статический импорт
- Класс Class
- Программно получить имя класса
- Изучаем Java для разработки под Android: внутренние классы
- Что вам нужно
- Что такое внутренний класс?
- Использование статических вложенных классов
- Советы по чистому коду новичкам в Java/Android
- Общие советы
- Наименование
- Функции
- Форматирование
- Классы
- Советы о разном
Классы и объекты
Java — это объектно-ориентированный язык, поэтому код в ваших программах будет состоять из объектов и классов.
Классы
Java позволяет создавать классы, которые представляют объекты из реального мира. Например, можно создать класс Car (автомобиль) или Animal (животное) и задать им различные свойства. Для класса Car логично создать такие свойства как двери, колёса, лобовое стекло и т.д. Имея класс Car, можно создать новые классы Легковушки, Грузовики, Автобусы, которые будут иметь все свойства класса Car, а также свои собственные свойства. У класса Animal соответственно можно задать свойства Лапы, Хвост, а затем создать наш любимый класс Cat, у которого будет ещё дополнительное свойство Усы. Иными словами, классы могут наследовать свойства от других классов. Родительский класс называется суперклассом. Внутри классов могут быть объявлены поля и методы.
Для объявления класса служит ключевое слово class. Вспомним стандартную строчку кода из Android-проекта:
Упрощённая общая форма для класса может иметь следующий вид:
В Java принято начинать имена класса с большой буквы. В классе могут быть несколько переменных и методов. Переменные, определённые внутри класса (не метода), называются переменными экземпляра или полями (fields). Код пишется внутри класса. Методы и переменные внутри класса являются членами класса.
Объекты
Новый объект (или экземпляр) создаётся из существующего класса при помощи ключевого слова new:
В большинстве случаев вы будете использовать такой способ. Пусть вас не удивляет, что приходится дважды использовать слово Cat, оно имеет разный смысл.
Слева от оператора присваивания = определяется имя переменной и его тип Cat. В правой части выражения происходит выделение памяти для нового экземпляра класса Cat и инициализируется экземпляр. Оператор присваивания присваивает переменной ссылку на только что созданный объект. Имена объектов не нужно начинать с большой буквы, как у класса. Так вы будете различать, где класс, а где экземпляр класса. Если имя экземпляра класса состоит из нескольких слов, то используется верблюжья нотация, когда все первые буквы слов, кроме первой, пишутся с большой — superBlackCat.
Если вы помните, при объявлении примитивных типов мы указывали нужный тип в самом начале.
Поэтому код Cat barsik также определяет его тип. Он не всегда может совпадать с именем класса.
В этом примере используется тип класса домашних любимцев Pet, а обращаемся к классу котов Cat.
Простой пример создания класса Box (коробка для кота):
При таком варианте Java автоматически присвоит переменным значения по умолчанию. Например, для int это будет значение 0. Но не всегда значения по умолчанию подойдут в вашем классе. Если вы создали переменную для описания количества лап у кота, то логично сразу присвоить значение 4. Поэтому считается хорошей практикой сразу присваивать нужные значения полям класса, не полагаясь на систему.
Вам нужно создать отдельный файл Box.java, в который следует вставить код, описанный выше. О том, как создавать новый файл для класса я не буду здесь расписывать.
Сам класс — это просто шаблон, заготовка. Чтобы ваше приложение могло использовать данный шаблон, нужно создать на его основе объект при помощи ключевого слова new:
Красивая получилась коробочка.
Объект catBox, объявленный в коде вашей программы, сразу займёт часть памяти на устройстве. При этом объект будет содержать собственные копии переменных экземпляра width, height, depth. Для доступа к этим переменным используется точка (.). Если мы хотим присвоить значение переменной width, то после создания объекта класса можете написать код:
Если мы хотим вычислить объём коробки, то нужно перемножить все значения размеров коробки:
Каждый объект содержит собственные копии переменных экземпляра. Вы можете создать несколько объектов на основе класса Box и присваивать разные значения для размеров коробки. При этом изменения переменных экземпляра одного объекта никак не влияют на переменные экземпляра другого объекта. Давайте объявим два объекта класса Box:
Когда мы используем конструкцию типа Box bigBox = new Box();, то в одной строке выполняем сразу два действия — объявляем переменную типа класса и резервируем память под объект. Можно разбить конструкцию на отдельные части:
Обычно такую конструкцию из двух строк кода не используют на практике, если нет особых причин.
Когда мы используем ключевое слово new и указываем имя класса, то после имени ставим круглые скобки, которые указывают на конструктор класса. О них поговорим позже.
Ключевое слово final
Поле может быть объявлено как final (финальное). Это позволяет предотвратить изменение содержимого переменной, по сути, это становится константой. Финальное поле должно быть инициализировано во время его первого объявления.
Теперь можно пользоваться переменной FILE_OPEN так, как если бы она была константой, без риска изменения их значений. Принято записывать имена заглавными буквами.
Кроме полей, final можно использовать для параметров метода (препятствует изменению в пределах метода) и у локальных переменных (препятствует присвоению ей значения более одного раза).
Также слово final можно применять к методам, чтобы предотвратить его переопределение.
Ещё один вариант использования ключевого слова final — предотвращение наследования класса. При этом неявно всего методы класса также становятся финальными. Поэтому нельзя одновременно объявить класс абстрактным и финальным, поскольку абстрактный класс является лишь шаблоном и только его подклассы реализуют методы.
Ключевое слово instanceof — Проверка принадлежности к классу
Иногда требуется проверить, к какому классу принадлежит объект. Это можно сделать при помощи ключевого слова instanceof. Это булев оператор, и выражение foo instanceof Foo истинно, если объект foo принадлежит классу Foo или его наследнику, или реализует интерфейс Foo (или, в общем виде, наследует класс, который реализует интерфейс, который наследует Foo).
Возьмём пример с рыбками, которые знакомы котам не понаслышке. Пусть у нас есть родительский класс Fish и у него есть унаследованные подклассы SaltwaterFish и FreshwaterFish. Мы можем протестировать, относится ли заданный объект к классу или подклассу по имени
Данная проверка удобна во многих случаях. В Android очень много классов, которые происходят от класса View — TextView, CheckBox, Button, имеющие свои собственные наборы свойств. И если имеется метод с параметром View, то при помощи instanceof можно разделить логику кода:
import — Импорт класса
Оператор import сообщает компилятору Java, где найти классы, на которые ссылается код. Любой сложный объект использует другие объекты для выполнения тех или иных функций, и оператор импорта позволяет сообщить о них компилятору Java. Оператор импорта обычно выглядит так:
За ключевым словом import следуют класс, который нужно импортировать, и точка с запятой. Имя класса должно быть полным, то есть включать свой пакет. Чтобы импортировать все классы из пакета, после имени пакета можно поместить .*.
В Android Studio импорт класса происходит автоматически при наборе кода. Также это срабатывает и при вставке кода. Если имена классов совпадают, то студия может запросить помощь. Тогда вам нужно вручную указать нужное полное имя класса.
Импорт позволяет избежать долгого набора имени класса. Без импорта нам пришлось бы писать все классы в коде программы полностью.
Статический импорт
Существует ещё статический импорт, применяемый для импорта статических членов класса или интерфейса. Это позволяет сократить количество кода. Например, есть статические методы Math.pow(), Math.sqrt(). Для вычислений сложных формул с использованием математических методов, код становится перегружен. К примеру, вычислим гипотенузу.
В данном случае без указания класса не обойтись, так как методы статические. Чтобы не набирать имена классов, их можно импортировать следующим образом:
После импорта уже нет необходимости указывать имя класса.
Второй допустимый вариант, позволяющий сделать видимыми все статические методы класса:
В этом случае вам не нужно импортировать отдельные методы. Но такой подход в Android не рекомендуется, так как требует больше памяти.
Класс Class
На первый взгляд, класс Class звучит как «масло масляное». Тем не менее, класс с таким именем существует и он очень полезен.
Программно получить имя класса
Иногда из программы нужно получить имя используемого класса. Для этого есть специальные методы getClass().getName() и другие родственные методы. Допустим, нам нужно узнать имя класса кнопки, на которую мы нажимаем в программе.
getSimpleName() возвращает только имя класса без пакета, другие методы вернут полное название.
Если нужно узнать имя класса активности, то достаточно кода:
Если вам известно имя класса, то можете получить сам класс:
Метод getSuperclass() возвращает имя суперкласса. Остальные несколько десятков методов не столь популярны.
Источник
Изучаем Java для разработки под Android: внутренние классы
В этом руководстве вы познакомитесь с концепцией внутренних классов в Java — тех классов, область действия и определение которых включены в другой класс. Вы также узнаете об анонимных внутренних классах, которые довольно часто используются при разработке с Android SDK.
Приложения Android написаны на Java, объектно-ориентированном языке программирования. В этом уроке вы узнаете о внутренних классах, когда и зачем их использовать и как они работают. Вы также узнаете, как динамически создавать новые объекты, используя анонимные внутренние классы.
Что вам нужно
Технически, вам не нужны никакие инструменты для завершения этого урока, но вам, безусловно, они понадобятся для разработки приложений для Android.
Для разработки приложений Android (или любых других приложений Java) вам необходима среда разработки для написания и создания приложений. Eclipse — это очень популярная среда разработки (IDE) для Java и предпочтительная IDE для разработки под Android. Он свободно доступен для операционных систем Windows, Mac и Linux.
Полные инструкции по установке Eclipse (включая поддерживаемые версии) и Android SDK см. На веб-сайте разработчиков Android .
Что такое внутренний класс?
Большинство классов в Java являются классами верхнего уровня. Эти классы и объекты, которые они определяют, являются автономными. Вы также можете создавать вложенные классы, чтобы четко инкапсулировать и определять подчиненные объекты, которые имеют значение только в контексте внешнего класса. Вложенные классы называются внутренними классами.
Внутренние классы могут иметь все возможности обычного класса, но их область действия ограничена. Внутренние классы имеют еще одно преимущество: они имеют полный доступ к классу, в который они вложены, — эта функция делает внутренние классы идеальными для реализации функций адаптера, таких как итераторы.
Вот пример класса верхнего уровня с двумя внутренними классами:
В этом примере класс User имеет два внутренних класса: LoginInfo и Preferences. Хотя все пользовательские данные и функциональные возможности могут быть определены в классе User, использование внутренних классов для разделения функциональных возможностей может облегчить чтение и обслуживание кода. Внутренние классы LoginInfo и Preferences также имеют доступ к защищенным / закрытым полям и методам, доступным в классе User, которые они иначе не могли бы иметь из-за безопасности, если бы они были определены как самостоятельные классы.
Однако важно помнить, что внутренние классы действительно существуют только для того, чтобы помочь разработчику организовать код; компилятор обрабатывает внутренние классы так же, как и любой другой класс, за исключением того, что внутренние классы имеют ограниченную область видимости и поэтому привязаны к классу, с которым они определены. Иными словами, вы не сможете использовать или создавать экземпляры классов LoginInfo или Preferences, кроме как с экземпляром класса User, но внутренние классы могут обращаться к любым полям или методам, доступным во внешнем классе User, по мере необходимости.
Использование статических вложенных классов
В частности, для вложенных классов используются статические вложенные классы. Статический внутренний класс определяет поведение, которое не привязано к конкретному экземпляру объекта, но применяется ко всем экземплярам. Например, мы могли бы добавить третий вложенный класс, на этот раз статический, в класс User для управления функциональностью, связанной с сервером:
Источник
Советы по чистому коду новичкам в Java/Android
Теме чистого кода на одном только habrahabr посвящено тысячи статей. Никогда бы не подумал, что захочу написать свою. Но после проведения компанией курсов для желающих связать карьеру с разработкой ПО, в частности разрабатывать под Android, мое мнение поменялось.
За основу статьи взяты советы из классики “Роберт К. Мартин: Чистый код”. Отобрал из них те, которые наиболее часто встречались в виде проблем у студентов. Приведенные советы написаны с учетом моего опыта разработки коммерческих Android приложений. Поэтому не ко всем Android-проектам приведенные ниже советы подойдут, я уже не говорю про другие системы.
Советы в основном приводил с примерами кода как НЕ НУЖНО делать. Как ни странно, у большинства студентов были одни и те же ошибки. Все примеры кода были придуманы, любые совпадения с реально существующим кодом случайны.
Общие советы
1. Код должен быть легко читаемым, понятным и очевидным
Программисты большую часть времени тратят на чтение и анализ написанного кода, а не на написание нового. Важно чтобы Ваш код был легко читаемым, понятным и с прогнозируемым поведением. Это позволит коллегам и Вам по прошествии времени затратить минимальное время на понимание того, что делает каждый кусок кода. Понятный код с прогнозируемым поведением позволит уменьшить вероятность ошибки при внесении изменений не автором кода.
2. При написании кода нужно придерживаться Java Code Conventions либо других спецификаций, принятых на проекте командой
Спецификацию можно сравнить с правилами оформления текста книги. Думаю, немногие захотят читать книгу, где каждый абзац отличается шрифтом, размером и цветом текста. Так и с кодом, куда легче читать оформленный в едином стиле код.
Наименование
1. Имена классов, функций, переменных и параметров должны передавать закладываемый в них смысл.
Довольно очевидный совет, но не всегда его придерживаются. Тут, в качестве примера, можно привести оглавление книги: если все главы названы просто «Главами», то понять в чем суть нельзя. Наименования классов, функций должны сообщать об их предназначении без погружения в детали реализации.
Наименования классов MyActivity, CustomView, MyAdapter говорит только об одном, что это не часть Android SDK и все. SecondActivity говорит, что это не часть Android SDK и о наличии в проекте еще одного Activity, но не факт =)
Правильно именовать классы: MainActivity, FoodsActivity, FoodCartActivity, PhoneInputView . MainActivity — очевидно, что основной разводящий экран приложения. FoodsActivity — экран с перечнем продуктов. PhoneInputView — компонент ввода номера телефона.
Следующие названия переменных бесполезны и нарушают правила наименования:
как и параметры конструктора:
2. Название публичного класса и файла должно совпадать.
Есть следующий класс
Название файла при этом оказывается “products_Activity.java”.
Это не правильно. Название публичного класса и java-файла должны совпадать.
3. В наименованиях нужно использовать только буквы латинского алфавита, никаких цифр, символов подчеркивания и дефисов. Исключения составляют наименования из стандартов (ГОСТ, ISO), символы подчеркивания для разделения слов в наименованиях констант.
Примеры выше: m_textview_1 . Часто вместо lastName пишут userName2 , что не правильно.
4. Не нужно использовать строчные “L” и “O” в качестве имен локальных переменных, т.к. их трудно отличить от “1” и “0”.
Надуманный пример, но что-то подобное я встречал в своей практике
В этой функции пузырьковой сортировки есть одна ошибка, сможете за секунды ее найти=)?
Такой код труден для чтения и при написании легко сделать ошибку, которую можно очень долго искать.
5. Не нужно указывать тип в суффиксе имен.
Вместо accountList нужно писать просто accounts . Это позволит в любое время изменить тип переменной без переименования самой переменной.
А еще ужаснее выглядит nameString, ageFloat .
Исключение составляют наследники классов Android SDK: Activity, Fragment, View, Uri и т.д. По названию NewsSynsService сразу понятно, что класс является «сервисом» и ответственен за синхронизацию новостей. Использование суффикса view в nameView, photoView позволяет легко отличить переменные, относящиеся к верстки, от остальных. Имена view обычно начинают с существительного. Но имена кнопок лучше начинать с глагола: buyButton
6. Имена могут и должны содержать термины из математики, названия алгоритмов, паттернов проектирования и т.д.
Увидев имя BitmapFactory , не автор кода сразу поймет смысл этого класса.
7. Не нужно указывать никакие префиксы при именовании.
Вместо m_user, mUser просто пишется user . Указывать префикс s для статических полей в современных IDE излишне.
Исходники Android SDK не являются здесь показателем в силу давности создания первых версий и наследования кодовой базы до наших дней.
s_ ни к чему в начале названия статического поля. К тому же название констант должно писаться прописными буквами:
8. В наименование классов нужно использовать существительные.
Классы это как объекты реального мира. Поэтому нужно использовать существительные для их названия: AccountsFragment, User, Car, CarModel .
Не нужно называть классы Manager, Processor, Data, Info , т.к. они имеют слишком общее значение. Лучше название класса длиной в два-четыре слова, чем просто Data .
9. Названия классов должны начинаться с прописной буквы.
Слова НЕ должны отделяться символом подчеркивания. Нужно следовать нотации CamelCase: GoodsFragment, BaseFragment
10. Используйте одно слово для каждой концепции.
Использование fetch, retrieve, get в одном классе сбивает с толку. Если класс назвали Customer , то имена переменных класса и параметров функций этого типа лучше называть customer , а не user .
Функции
1. Функция должна выполнять только одну “операцию”. Она должна выполнять ее хорошо. И ничего другого она делать не должна.
Под “операцией” следует понимать не одну математическую операцию или вызов другой функции, а действия на одном уровне абстракции. Чтобы определить, что функция выполняет более одной операции, следует попробовать извлечь из нее другую функцию. Если извлечение функции не дает ничего кроме простой перестановки кода, то значит разбивать дальше функцию не имеет смысла
К примеру есть функция setProducts класса ProductsAdapter :
Внутри функции происходит три основных операции: 1) фильтрация newProducts , 2) очистка и вставка новых значений в products , 3) обновление адаптера notifyDataSetChanged .
Фильтрация элементов newProducts должна происходить в другом методе и даже в другом в класса (например в presenter-е). В ProductsAdapter должны прийти уже отфильтрованные данные.
Лучше переделать код следующим образом:
Параметр newProducts содержит уже отфильтрованный список продуктов.
Можно еще строчки java products.clear(); products.addAll(newProducts);
вынести в отдельную функцию, но особого смысла я в этом не вижу. Лучше заменить notifyDataSetChanged на DiffUtil, это позволит обновлять ячейки в списке эффективнее
2. Размер функции должен быть 8-10 строк.
Функция размером в 3 экрана это не функция, это *****. У меня тоже не всегда получается ограничить размер функции 8-10 строками кода, но нужно стремится к этому. Пример по понятным причинам приводить не буду.
Функции большого размера трудно читать, модифицировать и тестировать. Разбив большую функцию на малые, легче будет понять смысл основной функции, так как мелкие детали будут скрыты. Выделяя функции можно увидеть и избавиться от дублирования кода в основной функции.
3. В теле функции все должно быть на одном уровне абстракции.
Вычисление значения локальной переменной errorMessage имеет более низкий уровень абстракции, чем остальной код внутри функции. Поэтому код java «Article » + article.getName() + » is incorrect» лучше вынести в отдельную функцию.
4. Наименовании функции и передаваемых параметров должно сообщать о том, что делает функция
Причиной выделения блока кода в отдельную функцию может являться желание в пояснении, что делает код. Для этого нужно дать подходящее название функции и параметрам функции.
Непонятно по какому полю будет происходить поиск, что передается на вход функции.
Лучше переделать в следующий вид:
Название функции говорит, что происходит поиск Product по полю id . На вход функция принимает не “null” значение. Если Product не найдется, то вернется “null”.
Роберт К. Мартин советует использовать параметры в качестве части названия функции:
Вызов функции может выглядеть так:
На проектах я такой способ не встречал. Лучше данный способ не использовать и писать полностью название:
5. Имена функций должны начинаться с глагола. Лучше длинное имя, чем не содержательное короткое.
Название start не сообщает, что именно стартует функция. Только заглянув в тело становится понятно, что функция открывает Activity.
А должно быть понятно предназначение функции уже по названию.
6. Вместо передачи в аргументы функции флага (boolean) лучше разбить функцию на две функции
Часто этот флаг является причиной увеличение размера функции при ветвлении логики выполнения в зависимости от значения флага. В таких случаях следует подумать о разбиении данной функции на две. Разное поведение функции в зависимости от переданного флага не всегда очевидно.
7. Дублирующий код следует выносить в отдельную функцию.
Код внутри setOnClickListener отличается только стилем. Этот код стоит вынести в отдельный метод:
8. Не передавайте и не возвращайте из функции “null” когда можно вернуть пустой объект.
Это позволит избежать ошибок NullPointerexception и не нужно будет в местах вызова писать проверки на null.
Kotlin поможет отучиться от вредной привычки передавать и возвращать null.)
Форматирование
1. Код в классе должен читаться сверху-вниз как газетная статья в порядке убывания уровня абстракции. Вначале идут публичные функции, затем приватные.
Основная идея совета в том, что при открытии файла программист начинает просматривать его сверху. Если вначале разместить все публичные функции, то легче будет понять основные операции с объектами класса, ответственность класса и где может использоваться. Данный совет подходит, когда проект строится на интерфейсах.
2. Связанные концепции/функции следует размещать рядом, чтобы не было необходимости постоянно перемещаться по файлу вверх-вниз. Если одна функция вызывает другую, то вызываемая функция должна располагаться под вызывающей функцией (если это возможно) и разделяться пустой строкой.
Данный совет может противоречить предыдущему. Выбрав приоритетный совет для команды, стоит придерживаться его на всем проекте.
3. Каждая последовательная группа строк кода должна представлять одну законченную мысль. Мысли отделяются друг от друга в коде с помощью пустых строк, только слишком злоупотреблять пустыми строчками не стоит
Связанная группа строк это как абзацы в статье. Для разделения абзацев используется красная строка или пустая строка. Так и в коде следует разделять разные по смыслу группы строк, используя пустые строки.
4. Переменные экземпляра класса, статические константы должны быть все в начале класса в одном месте.
В начале класса объявляются публичные константы, затем приватные константы и после них идут приватные переменные.
5. Используйте пробелы для повышения читаемости кода
- в математических операциях для отделения символа операции от операндов: int x = a + b;
- отделения параметров функций: private Article createNewArticle(String title, String description)
- передаваемых параметров функции: createNewArticle(title, description)
6. «Магические цифры, строки» должны быть вынесены в константы .
Пожалуй, самый популярный совет, но все равно продолжают его нарушать.
Лишь прочитав постановку задачи я понял, для чего в коде использовалось число “10”. Лучше это число вынести в константу и дать осмысленное имя.
Строки тоже стоит выносить в константы, особенно если используются в более чем одном месте.
Классы
1. Класс должен иметь одну “ответственность”, одну причину для изменения.
К примеру, наследники класса RecyclerView.Adapter должны отвечать за создание и связывание View с данным. В нем не должен находится код сортировки/фильтрации списка элементов.
Часто в файл с Activity добавляют класс RecyclerView.Adapter, что является не правильным.
2. Не нужны пустые методы или просто вызывающий метод из родительского класса
3. Если переопределяете какой-то метод без вызова метода родительского, то проверьте, что так можно делать.
Загляните в исходники родительских классов, документации. Переопределяемые методы жизненного цикла Activity, Fragment, View должны обязательно должны вызывать методы родительского класса.
Есть аннотация @CallSuper , предупреждающая о необходимости вызывать родительский метод при переопределении.
Советы о разном
1. Используйте средства Android Studio для улучшение качества Вашего кода.
Если она подчеркивает или выделяет код, значит что-то может быть не так. В Android Studio есть средства Rearrange, Reformat, Extract Method, Inline Method, анализаторы кода, горячие клавиши и т.д.
2. Не нужно комментировать каждый метод, код должен быть самодокументированным.
Следует отметить, что комментарии должны пояснять намерения и причины, а не поведение кода. Создавая комментарий, необходимо брать на себя ответственность о поддержании комментария в актуальном состоянии.
3. Строки, цвета, размеры должны быть в ресурсах (xml)
Не нужно писать:
Исключения составляют файлы из папок test, mock. Такие файлы не должны попадать в релизную версию
4. Не нужно передавать context в RecyclerView.Adapter. context можно получить из любой view. Элемент по клику в RecyclerView.Adapter должен меняться через notifyItemChanged, а не непосредственным изменением вьюшек в методе обработки нажатия
В методе onClick непосредственно происходит вызов bindViewHolder , что НЕ правильно.
5. В RecyclerView.ViewHolder вместо определения позиции объекта в списке нужно вызывать getAdapterPosition, а не передавать position из метода onBindViewHolder
Правильно писать:
Либо можно вместо позиции передавать ссылку на исходный объект с данными
6. Используйте Quantity Strings (Plurals) для множественного числа. Вместо написания в коде логики по подбору правильного окончания слов.
7. Не сохраняйте большие данные Bitmap в bundle. Размер bundle ограничен
8. Если прописать “id” в xml, то Android сам восстановит состояние стандартных View при повороте.
Источник