【问题标题】:Why is there an extra <E> in this generic method?为什么在这个泛型方法中有一个额外的 <E> ?
【发布时间】:2011-12-28 07:44:12
【问题描述】:

前段时间学过java泛型,现在在学集合,发现了一些看不懂的代码。代码如下:

static <E> List<E> nCopies(int n, E value)

来自java.util.Collections类。

我的问题是为什么会有:

<E> List<E>

不仅如此

List<E>

显然我遗漏了一些东西,有人可以为我澄清一下吗?

【问题讨论】:

    标签: java generics methods


    【解决方案1】:

    List&lt;E&gt; 是方法的返回类型,而&lt;E&gt; 是传入的类型(这是编译器根据E value 传递的内容推断出来的)。 p>

    static <E> List<E> someMethod(E myObject)
    {
        E objectOfMyType = myObject;
        List<E> myList = new ArrayList<E>();
        ...
        return myList; 
    }
    

    这将被称为:

    MyObject o = new MyObject();
    List<MyObject> myList = SomeClass.someMethod(o);
    

    恕我直言,方法的语法有点笨拙,但你有它。相关的 Oracle 教程在这里: http://download.oracle.com/javase/tutorial/extra/generics/methods.html

    【讨论】:

    • 我不明白为什么这是在 List 前面。我知道值是 E 类型,但是 List 前面的 让我感到困惑。谢谢你的回答。
    • @Иван,它是类型参数的声明。这类似于在方法中声明普通参数的方式,例如String myStringArg。例如,如果您有两个类型参数,它可能看起来像 &lt;E, TArg2&gt; 而不仅仅是 &lt;E&gt;
    • 再说一遍,不是因为它是静态的,而是因为它是通用的。
    • @Mark,同意——愚蠢的是将泛型类型参数声明放在返回类型和方法名称的前面的想法,这在某种程度上违反了一些先天语法感。
    • @Kirk:也许吧。从我的角度来看,告诉编译器您将在之前使用 E 作为类型参数是非常有意义的,而不是之后。
    【解决方案2】:

    &lt;E&gt; List&lt;E&gt; 中,第一个&lt;E&gt; 表示E 是一个类型参数。如果您没有指定它,那么 Java 会认为 E value 中的 E 引用了一个名为 E 的实际类,并要求您导入它。见generic methods

    【讨论】:

    • 为什么不能这样写:static List&lt;E&gt; nCopies(int n, &lt;E&gt; value)。像我上面写的那样,Java 编程语言的设计者这样做有什么问题吗?对我来说,表示泛型变量的方式更清晰。
    • 一个原因可能是如果您有多个类型为&lt;E extends F&gt; 的参数,它会重复;最好把它放在前面,然后在任何地方使用&lt;E&gt;
    【解决方案3】:

    &lt;E&gt; 需要告诉编译器您打算使用 E 作为类型参数,这与创建泛型类时的方式相同(例如 public interface List&lt;E&gt;)。

    由于没有关于接口或类名称超过一个字符的规则(仅约定),并且没有规则(仅约定)类型参数名称必须是一个字符,编译器不会知道您打算使用它类型参数而不是具体的类名。

    编辑

    很多人一直说这与静态方法直接相关。那不是真的。您也可以拥有一个对其自己的类型参数通用的实例方法(尽管通常,类型参数将与类类型参数相关)。

    这里有一个例子,你可以在哪里拥有这个:

    public class MyList<E> {
    
        public <N super E> MyList<N> createCopy() {
            //...
        }
    }
    

    此方法将允许您创建列表的副本,但不会限制您使用与您拥有的列表相同的类型,而是允许您使用超类型。例如:

    MyList<Integer> integers = createList(1, 2, 5);
    MyList<Number> numbers = integers.createCopy();
    

    【讨论】:

      【解决方案4】:

      您使用&lt;E&gt; 来代表您定义的方法。

      泛型最常见的例子是有一个这样的典型类:

      public class SomeClass<E> {
          ...
      }
      

      然后,当您创建该类的新对象时,您可以像这样直接定义类型:

      new SomeClass<String>();
      

      这样,该类中引用&lt;E&gt; 的任何方法都会将&lt;E&gt; 视为一个字符串。

      现在考虑一个静态方法(它不绑定到类的任何特定实例),为了典型化该方法,您使用另一种适用于方法的典型化,如下所示:

      static <E> List<E> nCopies(int n, E value)
      

      您在返回类型之前使用&lt;E&gt; 表示“此特定方法在执行时将考虑一些 E”。 &lt;E&gt; 将在你调用该方法时决定:

      nCopies(3, "a");
      

      在此示例中,&lt;E&gt; 将是一个字符串,因此返回类型将是一个List&lt;String&gt;

      最后,您甚至可以将它们混合使用:

      public class SomeClass<E> {
          public <F> void doSomething(E e, F f) {
              ...
          }
      }
      

      在这种情况下,如果您有 SomeClass 的实例,则 doSomething 方法中的 E 将始终为 String(对于该实例),但 F 可以是您想要的任何值。

      【讨论】:

        【解决方案5】:

        简单来说:表示E 不是一个类。如果E 是一个类(或接口),List&lt;E&gt; 将是一个有效的返回类型——尽管不推荐,一个类可以用单个字母命名(因为类型变量也可以用更多字母命名,即使使用现有的类名,混淆任何人:static &lt;Integer&gt; List&lt;Integer&gt; method() {...})。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-12-29
          • 2015-10-13
          • 1970-01-01
          • 1970-01-01
          • 2022-01-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多