Тема 14. Java Code Convention - BelyiZ/JavaCourses GitHub Wiki

Содержание:

  1. Не игнорируйте исключения
  2. Комментарии/Javadoc
  3. Короткие методы
  4. Локальные переменные
  5. Импорты
  6. Отступы
  7. Названия полей и методов
  8. Фигурные скобки
  9. Длина строки
  10. Согласованность
  11. Список литературы/курсов

Соблюдение Java Code Convention помогает быстро просмотреть код и понять его. Для увеличения скорости проверки, интеграции кода в общий проект помогут общие правила.

Данный материал содержит лишь незначительную выборку из общепринятых правил из Java Code Convention.

Не игнорируйте исключения

Игнорирование исключений создает скрытые проблемы, которые трудно распознать. Специфика в каждом конкретном случае зависит от ситуации. Приемлемые альтернативы:

  1. Перебрасывайте исключения к вызывающему методу.
void setServerPort(String value) throws NumberFormatException {
    serverPort = Integer.parseInt(value);
}
  1. Выбрасывайте исключения, соответственно вашему уровню абстракции.
void setServerPort(String value) throws ConfigurationException {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new ConfigurationException("Port " + value + " is not valid.");
    }
}
  1. Перехватите ошибку и замените соответствующее значение в блоке catch{}
/**
 * Устанавливаем порт. Если порт задан не правильно, будет использовано значение по умолчанию: 80
 */
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        serverPort = 80;  // default port for server
    }
}
  1. Перехватите ошибку и выбросьте RuntimeException. Это опасно: делайте это только если вам все равно случится ли эта ошибка.
/**
 * Устанавливаем порт. Если порт задан не правильно, будет выброшено исключение
 */
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        throw new RuntimeException("port " + value " is invalid, ", e);
    }
}

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

/**
 * Устанавливаем порт. Если порт задан не правильно, будет использовано прежнее значение
 */
void setServerPort(String value) {
    try {
        serverPort = Integer.parseInt(value);
    } catch (NumberFormatException e) {
        // Исключение будет проигнорировано.
        // порт останется без изменений
    }
}
  1. Не перехватывайте обобщенные исключения. Возможно появление исключения, которого вы не ожидали и, в итоге, ошибка будет отлавливаться на уровне приложения. В случае если кто-то добавит новый тип исключения, то компилятор не сможет вам помочь понять, что это другая ошибка. Существуют редкие исключения из этого правила: определенный тестовый код, или код верхнего уровня, где вы хотите перехватывать все типы ошибок (для того, чтобы предотвратить их отображение в пользовательском интерфейсе или, чтобы продолжить какую-то пакетную задачу).

Альтернативы обобщенным исключениям:

  • Перехватывайте каждое исключение отдельно в блоке catch, после одиночного try.
  • Измените ваш код для обработки ошибок с несколькими блоками try.
  • Перебросьте исключение. Во многих случаях нет необходимости обрабатывать все исключения на текущем уровне, просто позвольте методу перебросить их.

Комментарии/Javadoc

Каждый файл должен иметь объявление об авторских правах в самом начале. Далее идут объявления операторов package и import, причем каждый блок разделяется пустой строкой. За ними следуют объявления класса или интерфейса. Опишите, что делает класс в Javadoc-комментариях.

/*
 * Copyright (C) 2010 The Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package internal.foo;

import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * Подробное описание зачем нужен этот класс и как с ним работать
 */
public class Foo {
	
}

Каждый класс и нетривиальный public метод должен содержать Javadoc, по крайней мере с одной фразой, описывающей, что он делает. Фраза должна начинаться с описательного глагола 3-го лица. Примеры:

/** Returns the correctly rounded positive square root of a double value. */
static double sqrt(double a) {
}

/**
 * Constructs a new String by converting the specified array of
 * bytes using the platform's default character encoding.
 */
public String(byte[] bytes) {
}

Если метод призван выполнить какую-то задачу и его действия имеют важный эффект вне его самого, тогда его обязательно нужно задокументировать. Любой метод, получает пользу от Javadoc, неважно public он или нет. Public методы являются частью API, и поэтому они требуют описания в Javadoc. Для написания Javadoc следует придерживаться Sun Javadoc conventions.

Стиль TODO

  • Используйте комментарии TODO для кода, который является временным, краткосрочным, или хорошим, но не идеальным.
  • Комментарий должен включать в себя TODO:, например:
// TODO: удалить этот код после реализации метода ...
// TODO: этот хак был добавлен потому что ...
// TODO: необходимо написать реализацию этого метода, когда решится проблема с доступом по HTTP
  • Если комментарий имеет вид «в будущем сделать что-то», то необходимо убедиться, что он включает в себя конкретную дату (01 марта 2025 года), или конкретное событие «удалить после выхода версии 4.0».

Короткие методы

Методы должны быть небольшими и решающими конкретную задачу настолько, насколько это возможно. Нет строгого ограничения на длину метода. Но в случае если, метод превышает 40 строк, то, возможно, стоит его разбить на части, не нарушив структуры программы.

Локальные переменные

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

Локальные переменные должны объявляться в том месте, где впервые необходимо её использовать. Почти каждая локальная переменная нуждается в инициализаторе. Если еще нет информации как точно инициализировать переменную, то вам следует отложить её объявление, пока вы не будет точной информации.

Если переменная инициализируется при помощи оператора return метода, который выбрасывает проверяемое исключение, то она должна инициализироваться в блоке try. Если же переменная должна использоваться вне блока try, тогда она объявляется перед ним, неважно, есть ли информация как её точно нужно инициализировать:

// Объявляем переменную и пытаемся ее проинициализировать
Set s = null;
try {
	s = (Set) service.getCollection();
} catch(IllegalAccessException e) {
    throw new IllegalArgumentException(cl + " not accessible");
} catch(InstantiationException e) {
    throw new IllegalArgumentException(cl + " not instantiable");
}
// работаем с инициализированной переменной
s.addAll(Arrays.asList(args));

Этот случай можно обойти при помощи инкапсуляции блока try-catch в методе:

Set createSet(Class cl) {
    try {
   	 return (Set) service.getCollection();
   } catch(IllegalAccessException e) {
   	 throw new IllegalArgumentException(cl + " not accessible");
	} catch(InstantiationException e) {
   	 throw new IllegalArgumentException(cl + " not instantiable");
	}
}

// Работа с множеством
Set s = createSet(cl);
s.addAll(Arrays.asList(args));

Важно: Переменные в циклах должны объявляться внутри самого оператора.

for (int i = 0; i <= n; i++) {
	doSomething(i);
}
 
for (Iterator i = c.iterator(); i.hasNext(); ) {
	doSomethingElse(i.next());
}

Импорты

Порядок операторов импорта следующий:

  1. Сторонние импорты (com, junit, net, org).
  2. java и javax.

Для полного соответствия настройкам IDE, импорты должны иметь следующий вид:

  • Отсортированы по алфавиту внутри каждой группы.
  • Заглавные буквы должны быть впереди букв нижнего регистра (например, Z перед a).
  • Главные группы должны разделяться пустой строкой.

Цель наличия такого порядка:

  • Импорты, которые пользователи хотят видеть в первую очередь, находятся вверху.
  • Импорты, которые пользователи хотят видеть в последнюю очередь, находятся внизу (java).
  • Пользователи могут с легкостью использовать этот стиль.
  • IDE может придерживаться этого стиля.

Отступы

  • 4 пробела для блоков.
  • Табуляцию использовать не нужно.
  • 8 пробелов для переноса строк, включая вызовы функций и присваивания, например:
//Верно
Instrument i = 
        someLongExpression(that, wouldNotFit, on, one, line);

//Неверно
Instrument i =
    someLongExpression(that, wouldNotFit, on, one, line);

Названия полей и методов

  • Названия полей которые не относятся к static и public начинаются c «m».
  • Названия полей static начинаются с «s».
  • Названия полей и методов начинаются с буквы нижнего регистра.
  • Названия классов должны начинаться с заглавной буквы.
  • Названия полей пишутся в формате camelCase.
  • Названия полей public static final (константы) полностью в верхнем регистре, с использованием подчеркивания (ALL_CAPS_WITH_UNDERSCORES).
public class MyClass {
    public static final int SOME_CONSTANT = 42;
    public int publicField;
    private static MyClass sSingleton;
    int mPackagePrivate;
    private int mPrivate;
    protected int mProtected;
}
  • Рассматривайте сокращения и аббревиатуры как слова.
  • Имена должны передавать смысл.
Верно Неверно
xmlHttpRequest XMLHTTPRequest
getCustomerId getCustomerID
class Html class HTML
String url String URL
long id long ID
void executeOperation() void ExecuteOperations()

Фигурные скобки

  • Открывающие фигурные скобки не ставятся с отдельной строки. Они находятся в той же строке, что и код перед ними:
class MyClass {
    int func() {
   	 if (something) {
       	 // ...
    	} else if (somethingElse) {
       	 // ...
    	} else {
       	 // ...
    	}
	}
}
  • Фигурные скобки должны проставлены обязательно для оператора условия.
// Верно
if (condition) {
	body();
}
if (condition) body();

// Неверно:
if (condition)
	body();

// Неверно
if (condition) body();

Длина строки

  • Каждая строка текста в коде должна быть не длиннее 100 символов.

Согласованность

В случае внесения изменений в код необходимо принять общий стиль в котором написан код «до» и «после». Если какое-то из правил описанных выше нарушено в коде, который необходимо поддерживать, то стоит придерживаться стиля, несмотря на нарушение общепринятых правил. Изменять принятый стиль кода в проекте стоит только после согласования с командой.

Список литературы/курсов

  1. https://www.oracle.com/java/technologies/javase/codeconventions-contents.html

Тема 13. Паттерны | Оглавление | Тема 15. Java Collection Framework