Нарушение соглашения об именах дженериков 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);
}