Нарушение соглашения об именах дженериков java?
У меня есть интерфейс, декларация которого такова:
/**
* @param T - the type of entity.
* @param C - the type of entity container will be returned.
*/
public interface FindByNamedQuery<T extends Serializable, C extends Collection<T>> extends Command {
C executeNamedQuery(String namedQuery);
}
Интересно, могу ли я (должен) нарушить соглашение об именах Java, чтобы сделать это:
public interface FindByNamedQuery<ENTITY_TYPE extends Serializable, RETURNED_CONTAINER extends Collection<ENTITY_TYPE>> extends Command {
RETURNED_CONTAINER executeNamedQuery(String namedQuery);
}
Ответы
Ответ 1
Я начинаю не соглашаться с односимвольным соглашением, после его использования с середины 1990-х годов.
Я считаю читаемые имена более читабельными. Это полезно для понимания как реализации, так и интерфейса общих типов.
Проблема двусмысленности кажется завышенной для Java. Немногие имена классов - все в верхнем регистре. Константы не используются в том же контексте, что и имена классов.
Верно, что элементы @param JavaDoc могут предоставить более подробное описание. Но также верно, что JavaDocs не обязательно видны. (Например, есть поддержка содержимого в Eclipse, которая показывает имена параметров типа.)
Например, сравните:
public final class EventProducer<L extends IEventListener<E>,E>
implements IEventProducer<L,E> {
в
public final class EventProducer<LISTENER extends IEventListener<EVENT>,EVENT>
implements IEventProducer<LISTENER, EVENT> {
Хотя односимвольные имена были рекомендованы как соглашение Sun/Oracle, соглашения могут быть изменены. Последствия оспаривания этой конвенции незначительны. Если вы и ваша команда предпочитаете значащие имена для ваших параметров типа, я лично поеду за ним.
Изменить (2015)
Стиль Google для Java позволяет использовать как однобуквенные имена, так и многосимвольные имена классов, заканчивающиеся на T.
5.2.8 Введите имена переменных типов
Каждая переменная типа указана в одном из двух стилей:
-
Единая заглавная буква, необязательно сопровождаемая одной цифрой (например, E, T, X, T2)
-
Имя в форме, используемой для классов (см. раздел 5.2.2, "Имена классов" ), за которой следует заглавная буква T (примеры: RequestT, FooBarT).
Ответ 2
Интересно, могу ли я (должен) разорвать соглашение об именах java для этого:
Нет, этого следует избегать, так как становится легче путать параметры типа с константами и другими идентификаторами.
Вот цитата из официальной тропы по дженерикам:
Типовые обозначения именования
По соглашению введите имена параметров одиночные, заглавные буквы. Это резко контрастирует с условными соглашениями об именах, о которых вы уже знаете, и с полным основанием: без этого соглашения было бы трудно определить разницу между переменной типа и обычным классом или именем интерфейса.
Наиболее часто используемые имена параметров типа:
-
E
- Элемент (широко используется структурой коллекций Java) -
K
- ключ -
N
- Номер -
T
- Тип -
V
- Значение -
S
, U
, V
и т.д. - 2-й, 3-й, 4-й типы
Вы увидите, что эти имена используются во всем API Java SE и в остальной части этого руководства.
Ответ 3
Использование TDescription довольно распространено в С#. Он поддерживает имя T, но также является описательным в одно и то же время:
public interface FindByNamedQuery<
TEntityType extends Serialiazble,
TReturnedContainer extends Collections<TEntityType>> extends Command
{
TReturnedContainer executeNamedQuery(String namedQuery);
}
Как говорили другие, ALL_CAPS
почти всегда указывает константу.
IMO, "было бы трудно определить разницу между переменной типа и обычным именем класса или интерфейса. здесь не применяется, поскольку префикс Т легко идентифицирует его как переменную типа.
Опять же, это С#, но см. MSDN: соглашения о присвоении имен для общих файлов
Во всех остальных случаях официальный Руководства Microsoft для общих Соглашения об именах:
-
Укажите общие параметры типа с описательными именами, если только один имя письма полностью само собой пояснительное и описательное имя не добавит значения.
public interface ISessionChannel<TSession>
{...}
public delegate TOutput Converter<TInput,TOutput>(TInput from);
- Рассмотрим указания ограничений на параметр типа в имени параметра. Например, параметр, ограниченный ISession, может быть назван TSession.
Ответ 4
Компилятор может не жаловаться, но ваши товарищи по команде могут не оценить, что вы используете то, что выглядит постоянным в месте, где они ожидают параметр типа.
Ответ 5
Я думаю, что это проблема многих людей, использующих дженерики. Я не совсем согласен с утверждением Sun, что если вы используете полноценное имя, то оно будет путать с существующим именем класса или чем-то еще. В этом случае мы можем начинать имя заполнителя с помощью доллара следующим образом:
public class HashMap<$Key,$Value> implements Map<$Key,$Value>{}
Никто в своем здравом уме не называет класс, начинающийся со знака доллара. Знак доллара также используется для обозначения заполнитель многих шаблонов скорости языков, расположений, spring и т.д. Я думаю, что это путь.
У меня есть более подробные сведения об этом и рассуждения о том, что вам не нужно использовать одну буквенную нотацию в моем сообщении в блоге, если кто-то заинтересован.
http://readsethu.wordpress.com/2012/05/23/a-generic-class-and-why-is-it-confusing/
Ответ 6
Как Аллен до, мой совет приходит больше от С# (который я использую широко с 5 месяцев), чем Java (с которым я играл, но он никогда не заходил очень далеко...), но я нахожу код Java и С# очень похожим по духу (то есть, если сравнивать, скажем, с С++)
В любом случае при использовании С#/Java generic (или шаблона С++) на простом типе я обычно использую T:
// C++
template<typename T>
class MyClass { /* ... */ } ;
// C#
public MyClass<T> { /* etc. */ }
// Java
public MyClass<T> { /* etc. */ }
Обычно тип T идет с классом, поэтому нет необходимости описывать его больше.
Но когда действительно описывающий тип добавляет ясность кода, я делаю это.
Или когда у меня есть два или более типа в одном и том же объявлении generic/template, это помогает сделать разницу между двумя типами. Например (пример реальной жизни в С#):
// C++
template<typename T_Data, typename T_Underlying>
class MyClass { /* ... */ } ;
// C#
public MyClass<T_Data, T_Underlying> { /* etc. */ }
// Java
public MyClass<T_Data, T_Underlying> { /* etc. */ }
Таким образом, легко сделать разницу между двумя именами в коде, где T
и U
, ну... kinda anonymous: для тех, кто использует Visual С++, происходит отладка внутри Dinkumware STL код, полный T
, _U
, и другие однобуквенные типы имен могут быть весьма неприятными... Я думаю, что это касается кода С# или Java.
Вы заметите, что в каждом случае (С++, Java или С#) я не соблюдаю соглашение где-то в тим-приложениях: Причина в том, что иногда вам просто нужно попробовать что-то другое вместо того, чтобы следовать за стадом, даже если, в конце концов, вы обнаружите, что ошибаетесь.
В данном случае нарушение соглашения об именах не является критическим (в Java есть худшие проблемы, чем это мелкое преступление), и, наконец, вы узнаете лично и точно, ПОЧЕМУ это неправильно, вместо цитирования старые документы.
И если вы найдете в конце концов, вы правы, ну...
Ответ 7
Я бы назвал переменные типа, подобные типам, в верблюжьей оболочке, но с префиксом "_".
public interface FindByNamedQuery
<_EntityType extends Serialiazble,
_ReturnedContainer extends Collections<_EntityType>>
extends Command
{
_ReturnedContainer executeNamedQuery(String namedQuery);
}