【问题标题】:Breaking java generics naming convention? [duplicate]打破java泛型命名约定? [复制]
【发布时间】:2011-09-02 20:35:58
【问题描述】:

我有一个接口,声明如下:

/**
* @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);
}

【问题讨论】:

  • 你已经有了你的@param标签来给用户关于类型参数的信息。
  • 我的目的是让声明更具描述性。
  • 您有正确的想法,使其更具描述性,将类型参数限制为 1 个字符,因为这很愚蠢,IMO。您提出的约定的唯一错误是ALL_CAPS 表示常量。
  • @ColinD,您对方法的参数也有相同的要求,但您不会到处命名abc 等。

标签: java generics naming-conventions


【解决方案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 style for Java 允许以 T 结尾的单字母名称和多字符类类名称。

5.2.8 Type variable names

每个类型变量都以两种样式之一命名:

  • 单个大写字母,可选后跟单个数字(例如 E、T、X、T2)

  • 用于类的名称(参见第 5.2.2 节,类名称),后跟大写字母 T(例如:RequestT、 FooBarT)。

【讨论】:

  • 我一般同意,但我认为添加“类型”后缀是我听过的最佳解决方案。对我来说,这似乎比添加“T”更清楚,因为它不那么深奥。使用全部大写和下划线是一个奇怪的选择,因为它是如此不同并且通常用于常量和枚举。它不会与其他任何东西混淆,因为它的怪异会要求你弄清楚它到底是什么。但当“类型”更清晰时,这似乎是不必要的认知开销。
  • @Drew - 可以有名称相同的实际类型,并在相同的上下文中使用。这会使识别一个类型是否是参数化类型变得更加困难。
【解决方案2】:

我想知道我是否可以(应该)打破 java 命名约定来做到这一点:

不,应该避免这种情况,因为它更容易将类型参数与常量和其他标识符混淆。

这是来自the official trail on generics的引述:

类型参数命名约定

按照惯例,类型参数名称是单个大写字母。这与您已经知道的变量命名约定形成鲜明对比,并且有充分的理由:没有这个约定,很难区分类型变量和普通类或接口名称之间的区别.

最常用的类型参数名称有:

  • E - 元素(被 Java 集合框架广泛使用)
  • K - 密钥
  • N - 号码
  • T - 类型
  • V - 值
  • S,U,V 等 - 第 2、3、4 种类型

您将在整个 Java SE API 和本教程的其余部分看到这些名称。

【讨论】:

  • C# 避免混淆类型变量和类/接口名称的方式对我来说似乎更合理。它使代码更容易理解,尤其是在存在多个类型参数的情况下。
  • 区分类型和类名有多难?严重地。我的意思是,大多数时候你直接从 IDE 中读取代码(它用不同的颜色突出显示通用名称)。此外,如果您是从一个笨拙的文本编辑器中阅读它,只需查看类的签名并记住几分钟,哪些名称指的是泛型类型,这很容易。
  • 有时不也是一种约定吗? stackoverflow.com/questions/643906/… “Void 已成为您不感兴趣的通用参数的约定。”
  • @barlop,我认为您将类型 argument 与类型 parameter 混淆了。
  • @Cristian,+1,这些类型的“问题”通常是类和/或方法过大的症状。
【解决方案3】:

使用 TDescription 在 C# 中很常见。它保留了 T 名称,但同时也是描述性的,如下所示:

public interface FindByNamedQuery<
    TEntityType extends Serialiazble, 
    TReturnedContainer extends Collections<TEntityType>> extends Command 
{     
    TReturnedContainer executeNamedQuery(String namedQuery); 
} 

正如其他人所说,ALL_CAPS 几乎总是表示一个常数。

IMO,“很难区分类型变量和普通类或接口名称。” 在这里不适用,因为 T 前缀很容易将其识别为类型变量.

同样,这是 C#,但请参阅 MSDN: Naming Conventions For Generics

在所有其他情况下,官方 Microsoft 通用指南 命名约定是:

  • 用描述性名称命名泛型类型参数,除非单个 字母名称完全是自我 解释性和描述性名称 不会增加价值。

    public interface ISessionChannel<TSession> 
    {...}
    
    public delegate TOutput Converter<TInput,TOutput>(TInput from);
    
  • 考虑在参数名称中指明类型参数上的约束。例如,约束到 ISession 的参数可以称为 TSession。

【讨论】:

  • 我也使用抽象类的前缀,接口蚂蚁的前缀 T 泛型类型的前缀对我来说似乎有效;-)
【解决方案4】:

编译器可能不会抱怨,但你的队友可能不喜欢你在他们期望类型参数的地方使用看起来是常量的东西。

【讨论】:

    【解决方案5】:

    我认为这是许多使用泛型的人的抱怨。我不太同意 Sun 的说法,即如果您使用完整的名称,那么它将与现有的类名或其他名称混淆。在这种情况下,我们可以像这样以美元开头的占位符名称:

    public class HashMap<$Key,$Value> implements Map<$Key,$Value>{}
    

    没有一个理智的人会命名一个以美元符号开头的类。并且美元符号也用于表示许多模板语言velocity,struts,spring等的占位符。我认为这是要走的路。

    如果有人感兴趣,我已经获得了更多关于此的详细信息以及不必在我的博客文章中使用单个字母符号的原因。

    http://readsethu.wordpress.com/2012/05/23/a-generic-class-and-why-is-it-confusing/

    【讨论】:

    • 感谢您的回答。自从我发布这个问题以来已经有一段时间了。我发现在Android库中,类型参数的命名方式与类名相同(例如:AsyncTask&lt;Params, Progress, Result&gt;
    【解决方案6】:

    Allen before 一样,我的建议更多来自 C#(我从 5 个月以来就广泛使用它)而不是 Java(我玩过它,但它从未走得太远......),但我发现 Java 和 C# 代码相当本质上相似(也就是说,当与 C++ 进行比较时)

    无论如何,当在简单类型上使用 C#/Java 泛型(或 C++ 模板)时,我通常使用 T:

    // C++
    template<typename T>
    class MyClass { /* ... */ } ;
    // C#
    public MyClass<T> { /* etc. */ }
    // Java
    public MyClass<T> { /* etc. */ }
    

    通常类型T是跟类一起的,所以不用多描述了。

    但是当真正描述类型增加了代码的清晰度时,我会这样做。

    或者当我在同一个泛型/模板声明中有两个或多个类型时,有助于区分两种类型。例如(C# 中的真实示例):

    // 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. */ }
    

    这样,很容易区分代码中的两个类型名,TU 是,嗯...有点匿名:对于那些使用 Visual C++ 的人,在 Dinkumware 的 STL 中进行调试充满T_U 和其他单字母类型名的代码可能会非常令人沮丧……我想C# 或Java 代码也是如此。

    您会注意到,在每种情况下(C++、Java 或 C#),我在类型命名的某处都没有遵循约定:原因是有时,您只需要尝试其他方法而不是随波逐流,就算最后你会发现自己错了。

    在当前情况下,违反命名约定并不严重(Java 中的问题比这种小罪还严重),最后,您将亲自了解为什么它是错误的,而不是引用旧文件。

    如果你最终发现你是对的,那么......

    【讨论】:

      【解决方案7】:

      我会命名与类型类似的类型变量,采用驼峰式大小写,但以“_”为前缀。

      public interface FindByNamedQuery
          <_EntityType extends Serialiazble, 
           _ReturnedContainer extends Collections<_EntityType>> 
          extends Command 
      {
          _ReturnedContainer executeNamedQuery(String namedQuery);
      }
      

      【讨论】:

      • 如果我不喜欢命名约定中的某些东西,那就是命名以_开头的私有字段,这是一样的(只是一个意见)。
      猜你喜欢
      • 1970-01-01
      • 2014-04-30
      • 1970-01-01
      • 2018-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-13
      相关资源
      最近更新 更多