【问题标题】:Java: Generics syntaxJava:泛型语法
【发布时间】:2010-08-22 15:55:34
【问题描述】:

这可以返回一个整数列表:

public List<Integer> GetIListImpl() {
    return new ArrayList<Integer>();
}

但是如果我想让调用者指定泛型类型呢?像这样的东西,虽然在语法上我不知道该怎么做:

public List<T> GetIListImpl<T>() {
    return new ArrayList<T>();
}

用法是:

    List<String> = GetIListImpl<String>();

【问题讨论】:

  • Re: GetIListImpl - 这看起来像 C# 命名约定。在 Java 中,它可能是例如getListImpl.

标签: java generics


【解决方案1】:

关于参数化类型的通用 static 工厂方法

您似乎想编写方便的工厂方法来实例化泛型集合。

您可以编写如下泛型方法:

public static <T> List<T> newArrayList() {
    return new ArrayList<T>();
}
public static <K,V> Map<K,V> newHashMap() {
    return new HashMap<K,V>();
}

那么你可以简单地写:

// absolutely type-safe!!! no compilation warnings at all!!!

List<String> names = newArrayList();

List<Integer> nums = newArrayList();

Map<String, List<String>> map = newHashMap();

请注意,在某些情况下,上述方法不必是 static,您可以选择在方法中省略实现 class 的名称,而仅使用 interface 名称(例如 @987654352 @, newMap)。


Effective Java 2nd Edition 的认可

这种泛型类型推断static工厂方法其实是Effective Java 2nd Edition认可的;它有幸成为书中讨论的第一个项目。

以下是来自第 1 项的相关引用:考虑static 工厂方法而不是构造函数:

static 工厂方法的第四个优点是它们减少了创建参数化类型实例的冗长性。

当您调用参数化类的构造函数时,不幸的是,您必须指定类型参数,即使它们在上下文中是显而易见的。这通常需要您快速连续提供两次类型参数:

    Map<String,List<String>> m = 
        new HashMap<String,List<String>>();

随着类型参数的长度和复杂性的增加,这种多余的规范很快就会变得很痛苦。但是,使用static 工厂,编译器可以为您找出类型参数。这称为类型推断。例如,假设HashMap 提供了这个static 工厂:

    public static <K,V> HashMap<K,V> newInstance() {
        return new HashMap<K,V>();
    }

然后你可以用这个简洁的替代方案替换上面冗长的声明:

    Map<String,List<String>> m = HashMap.newInstance();

不幸的是,在 1.6 版中,HashMap 等标准集合实现没有 static 工厂方法,但您可以将这些方法放在您自己的实用程序类中。更重要的是,您可以在自己的参数化类中提供此类 static 工厂。

该项目还规定了这些static 工厂方法的通用命名约定:

  • getInstance - 返回一个由参数描述的实例 […]
  • newInstance - 与 getInstance 类似,但它保证返回的每个实例都与其他实例不同。
  • newType - 与 newInstance 类似,但在工厂方法位于不同类中时使用。 Type 表示工厂方法返回的对象类型。

关于显式类型参数

在大多数情况下,您不必显式提供类型参数,因为 Java 泛型类型推断系统通常可以确定您需要什么。

然而,要提供显式类型参数,语法是将其放在之前方法名称(而不是之后)。下面是一个使用显式参数调用来自java.util.Collections 的泛型方法&lt;T&gt; List&lt;T&gt; emptyList() 的示例:

Collections.<String>emptyList();
// Collections.emptyList<String>(); // DOES NOT COMPILE

请注意,泛型方法调用的显式类型参数化的语法怪癖是您必须限定类型(如果 static)或您正在调用该方法的对象,甚至如果不是显式参数化,则可以省略它们。


参考文献


附录:来自 Guava 的集合工厂方法

需要注意的是Guava实际上已经为Java Collections Framework中的类型提供了static工厂方法:

来自主package com.google.common.collect:

其实本着Effective Java 2nd Edition推荐的精神,Guava自己的集合不提供public构造函数,而是提供static create()工厂方法:

库的其余部分还提供了许多非常有用的功能。

【讨论】:

  • 在这种情况下,依赖现有的库不是更好吗?如commons.apache.org/collections
  • @Colin:Apache Commons Collection 没有通用化。
  • 但是Guava 是泛型的,它有这些辅助方法:例如Lists.newArrayList() 和Maps.newHashMap()。
  • 很公平。它可能不是对问题的直接回答,但由于它是该原则的常见应用,因此值得一提。
  • 截至 2014 年,上述大多数建议都已过时,因为 Java 7 引入了菱形语法并消除了对静态工厂方法的需求。
【解决方案2】:

语法是

public static <T> List<T> getIListImpl() {
    return new ArrayList<T>();
}

你很亲密。

而且用法更像:

MyClass.<Integer>getIListImpl();

【讨论】:

  • 当我尝试这种用法时,我得到一个编译器错误:'令牌“(”的语法错误,此令牌之后的表达式'。代码:List&lt;Integer&gt; list = GetIListImpl&lt;Integer&gt;();我做错了什么?跨度>
  • 错误一定在其他地方。你能粘贴你的代码吗?
【解决方案3】:

首先你必须在方法而不是类级别上指定泛型类型。

class Test
{
    public static <T> List<T> getListImpl() {
        return new ArrayList<T>();
    }
}

看Generic类型参数的位置,它在方法名之前。对于类,它位于类名后面。因此,不要在方法名称之后添加泛型类型,而是像这样调用它

List<Integer> l = Test.<Integer>getListImpl()

您必须使用类名或实例来调用它。编译器不喜欢以下内容:

List<Integer> l = <Integer>getListImpl()

有时它可以在没有泛型类型的情况下工作

List<Integer> l = getListImpl()

这里比较类和方法泛型

class Test<T>{}//Generic after class name
T<Integer> t = new T<Integer>();
void <T> methName(){}//Generic before method name
t.<Integer>methName();

【讨论】:

  • 抱歉学究气,但这是一种方法而不是一种功能。
  • @Noel M 已修复。我倾向于将函数用于方法和函数。
猜你喜欢
  • 2012-08-29
  • 2015-10-08
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
  • 1970-01-01
  • 2015-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多