или в одиночных точках, где переменная экземпляра не находится в верхнем регистре.
Вопрос в том, что является правильным способом объявить эти типы переменных (например, журнал и экземпляр)?
Ответ 2
Диалог на этом, кажется, является антитезой беседы о наименовании классов interface
и abstract
. Я нахожу это тревожным и думаю, что решение проходит гораздо глубже, чем просто выбирать одно соглашение об именах и всегда использовать его с static final
.
Аннотация и интерфейс
При именовании интерфейсов и абстрактных классов принятое соглашение превратилось в не префикс или суффикс вашей abstract class
или interface
с любой идентифицирующей информацией, которая указывала бы, что это что-то иное, чем класс.
public interface Reader {}
public abstract class FileReader implements Reader {}
public class XmlFileReader extends FileReader {}
Разработчик, как говорят, не должен знать, что указанные классы abstract
или interface
.
Статический финал
Мои личные предпочтения и убеждения заключаются в том, что мы должны следовать аналогичной логике, ссылаясь на переменные static final
. Вместо этого мы оцениваем его использование при определении того, как его назвать. Кажется, что все аргументы в верхнем регистре - это то, что было несколько слепо принято на языках C и С++. По моему мнению, это не оправдание продолжения традиции на Java.
Вопрос о намерении
Мы должны спросить себя, что является функцией static final
в нашем собственном контексте. Вот три примера того, как static final
можно использовать в разных контекстах:
public class ChatMessage {
//Used like a private variable
private static final Logger logger = LoggerFactory.getLogger(XmlFileReader.class);
//Used like an Enum
public class Error {
public static final int Success = 0;
public static final int TooLong = 1;
public static final int IllegalCharacters = 2;
}
//Used to define some static, constant, publicly visible property
public static final int MAX_SIZE = Integer.MAX_VALUE;
}
Не могли бы вы использовать все прописные буквы во всех трех сценариях? Абсолютно, но я думаю, можно утверждать, что это умаляет цель каждого. Итак, рассмотрим каждый случай индивидуально.
Назначение: Частная переменная
В случае вышеприведенного примера Logger
регистратор объявляется как закрытый и будет использоваться только внутри класса или, возможно, для внутреннего класса. Даже если он был объявлен в protected
или package
видимости, его использование одно и то же:
public void send(final String message) {
logger.info("Sending the following message: '" + message + "'.");
//Send the message
}
Здесь нам все равно, что Logger
является переменной-членом static final
. Это может быть просто переменная экземпляра final
. Мы не знаем. Нам не нужно знать. Все, что нам нужно знать, это то, что мы регистрируем сообщение для регистратора, предоставленного экземпляром класса.
public class ChatMessage {
private final Logger logger = LoggerFactory.getLogger(getClass());
}
Вы не назовете его Logger
в этом сценарии, так почему вы должны называть его прописным, если он был static final
? Его контекст или намерение одинаковы в обоих обстоятельствах.
Примечание. Я изменил свою позицию на видимость package
, потому что она больше похожа на форму доступа public
, ограниченную уровнем package
.
Назначение: Enum
Теперь вы можете сказать, почему вы используете static final
целые числа как enum
? Это дискуссия которая все еще развивается, и я бы даже сказал, что это противоречиво, поэтому я постараюсь не допустить этого обсуждения надолго, зайдя в него. Однако было бы предложено реализовать следующий принятый шаблон перечисления:
public enum Error {
Success(0),
TooLong(1),
IllegalCharacters(2);
private final int value;
private Error(final int value) {
this.value = value;
}
public int value() {
return value;
}
public static Error fromValue(final int value) {
switch (value) {
case 0:
return Error.Success;
case 1:
return Error.TooLong;
case 2:
return Error.IllegalCharacters;
default:
throw new IllegalArgumentException("Unknown Error value.");
}
}
}
Существуют вариации выше, которые достигают той же цели, что и явное преобразование enum->int
и int->enum
. В области потоковой передачи этой информации по сети, встроенная сериализация Java просто слишком многословна. Простые int
, short
или byte
могут сэкономить огромную полосу пропускания. Я мог бы вникать в длительное сравнение и отличие от плюсов и минусов enum
vs static final int
, связанных с безопасностью, удобочитаемостью, ремонтопригодностью и т.д.; к счастью, это выходит за рамки этого обсуждения.
В нижней строке это, иногда static final int
будет использоваться как a enum
.
Если вы можете заставить себя согласиться с тем, что приведенное выше утверждение верно, мы можем последовать этому обсуждению стиля. При объявлении enum
принятый стиль говорит о том, что мы не делаем следующее:
public enum Error {
SUCCESS(0),
TOOLONG(1),
ILLEGALCHARACTERS(2);
}
Вместо этого мы делаем следующее:
public enum Error {
SUCCESS(0),
TOOLONG(1),
ILLEGALCHARACTERS(2);
}
Если ваш блок целых чисел static final
является свободным enum
, то почему вы должны использовать для него другое соглашение об именах? Его контекст или намерение одинаковы в обоих обстоятельствах.
Назначение: статическое, постоянное, общедоступное свойство
Этот пример использования, пожалуй, самый пасмурный и спорный из всех. Пример использования статического константного размера - это место, где это чаще всего встречается. Java устраняет необходимость в sizeof()
, но есть моменты, когда важно знать, сколько байтов займет структура данных.
Например, считайте, что вы пишете или читаете список структур данных в двоичном файле, а формат этого двоичного файла требует, чтобы общий размер блока данных был вставлен перед фактическими данными. Это распространено, так что читатель знает, когда данные останавливаются в сценарии, что есть больше, не связанных, данных, которые следует. Рассмотрим следующий формат файла:
File Format: MyFormat (MYFM) for example purposes only
[int filetype: MYFM]
[int version: 0] //0 - Version of MyFormat file format
[int dataSize: 325] //The data section occupies the next 325 bytes
[int checksumSize: 400] //The checksum section occupies 400 bytes after the data section (16 bytes each)
[byte[] data]
[byte[] checksum]
Этот файл содержит список объектов MyObject
, сериализованных в поток байтов и записанных в этот файл. Этот файл имеет 325 байт объектов MyObject
, но без знания размера каждого MyObject
у вас нет способа узнать, какие байты принадлежат каждому MyObject
. Итак, вы определяете размер MyObject
на MyObject
:
public class MyObject {
private final long id; //It has a 64bit identifier (+8 bytes)
private final int value; //It has a 32bit integer value (+4 bytes)
private final boolean special; //Is it special? (+1 byte)
public static final int SIZE = 13; //8 + 4 + 1 = 13 bytes
}
Структура данных MyObject
будет занимать 13 байтов при записи в файл, как определено выше. Зная это, читая наш двоичный файл, мы можем динамически определить количество MyObject
объектов в файле:
int dataSize = buffer.getInt();
int totalObjects = dataSize / MyObject.SIZE;
Это, по-видимому, типичный случай использования и аргумент для всех заглавных констант static final
, и я согласен, что в этом контексте все прописные буквы имеют смысл. Вот почему:
В Java нет класса struct
, такого как язык C, но struct
- это просто класс со всеми открытыми членами и без конструктора. Это просто данные struct
. Итак, вы можете объявить class
в struct
следующим образом:
public class MyFile {
public static final int MYFM = 0x4D59464D; //'MYFM' another use of all uppercase!
//The struct
public static class MyFileHeader {
public int fileType = MYFM;
public int version = 0;
public int dataSize = 0;
public int checksumSize = 0;
}
}
Позвольте мне предисловие к этому примеру, заявив, что лично я не буду разбираться таким образом. Вместо этого я предлагаю неизменяемый класс, который обрабатывает синтаксический анализ внутренне, принимая в качестве аргументов конструктора переменные ByteBuffer
или все 4. Тем не менее, доступ (установка в этом случае), этот элемент struct
будет выглядеть примерно так:
MyFileHeader header = new MyFileHeader();
header.fileType = buffer.getInt();
header.version = buffer.getInt();
header.dataSize = buffer.getInt();
header.checksumSize = buffer.getInt();
Это не static
или final
, но они являются публично открытыми участниками, которые могут быть напрямую установлены. По этой причине, я думаю, что когда участник static final
публично раскрывается, имеет смысл полностью загладить его. Это тот момент, когда важно отличить его от общедоступных нестатических переменных.
Примечание. Даже в этом случае, если разработчик попытался установить переменную final
, они будут встречены либо с ошибкой IDE, либо с компилятором.
Резюме
В заключение вы предпочтете, что соглашение, которое вы выбираете для переменных static final
, будет вашим предпочтением, но я твердо верю, что контекст использования должен сильно повлиять на ваше дизайнерское решение. Моя личная рекомендация состояла бы в том, чтобы следовать одной из двух методологий:
Методология 1: оценка контекста и намерения [highly subjective; logical]
- Если это переменная
private
, которая должна быть неотличимой от переменной экземпляра private
, тогда назовите их одинаково. все строчные буквы
- Если это намерение состоит в том, чтобы служить в качестве типа блока стиля
enum
стиля static
, тогда назовите его так же, как и enum
. pascal case: начальная шапка каждое слово
- Если это намерение состоит в том, чтобы определить какое-либо общедоступное, постоянное и статическое свойство, то пусть оно выделяется, делая все заглавные буквы
Методология 2: Частный vs Публичный [objective; logical]
Методология 2 в основном конденсирует свой контекст в видимость и не оставляет места для интерпретации.
- Если он
private
или protected
, тогда он должен быть строчным.
- Если он
public
или package
, тогда он должен быть прописным.
Заключение
Вот как я просматриваю соглашение об именах переменных static final
. Я не думаю, что это то, что может или должно быть вложено в один улов. Я считаю, что вы должны оценить его намерение, прежде чем принимать решение о его названии.
Однако основная цель должна заключаться в том, чтобы попытаться оставаться последовательным на протяжении всего объема вашего проекта/пакета. В конце концов, это все, что вы контролируете.
(Я ожидаю, что вас встретит сопротивление, но также надеюсь собрать некоторую поддержку со стороны сообщества в этом подходе. Независимо от вашей позиции, пожалуйста, держите его гражданским, когда вы отказываетесь, критикуете или одобряете этот стиль.)