Java:泛型语法

17

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

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

但是如果我想让调用者指定泛型类型怎么办?就像这样,尽管我不确定如何在语法上实现:

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

使用方法如下:

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

8
关于GetIListImpl - 这看起来像是C#的命名惯例。在Java中,可能会用getListImpl - polygenelubricants
3个回答

25

关于参数化类型的通用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();

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

《Effective Java第二版》的认可

这种泛型类型推断的static工厂方法实际上得到了《Effective Java第二版》的认可;它有着独特的特权,成为了该书讨论的第一条内容。

以下是来自第1条:考虑使用static工厂方法代替构造器的相关引用:

A fourth advantage of static factory methods is that they reduce the verbosity of creating parameterized type instances.

When you invoke the constructor of a parameterized class, unfortunately you must specify the type parameters even if they're obvious from context. This typically requires you to provide the type parameters twice in quick succession:

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

This redundant specification quickly becomes painful as the length and complexity of the type parameters increase. With static factories, however, the compiler can figure out the type parameters for you. This is known as type inference. For example, suppose that HashMap provided this static factory:

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

Then you could replace the wordy declaration above with this succinct alternative:

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

Unfortunately the standard collection implementations such as HashMap do not have static factory methods as of release 1.6, but you can put these methods in your own utility class. More importantly you can provide such static factories in your own parameterized classes.

该项还规定了这些static工厂方法的常见命名约定:

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

关于显式类型参数

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

尽管如此,如果要提供显式类型参数,则语法是将其放在方法名称之前(而不是之后)。以下是使用显式参数调用java.util.Collections中的泛型方法<T> List<T> emptyList()的示例:

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

注意,泛型方法调用的显式类型参数化的语法怪癖是,即使它们在非显式参数化时可以省略,但您必须限定类型(如果是static)或调用方法的对象。请注意保留"{{"和"}}"和HTML标签。

参考文献


附录:Guava提供的集合工厂方法

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

从主要的package com.google.common.collect中:

实际上,按照《Effective Java第二版》的建议,Guava自己的集合不提供公共构造函数,而是提供静态的create()工厂方法: 此外,该库还提供了许多非常有用的功能。

在这种情况下,依赖于现有的库(例如 http://commons.apache.org/collections/)会更好吧? - Colin Hebert
3
@Colin:Apache Commons Collection 没有泛型化。 - polygenelubricants
1
但是 Guava 是泛型的,而且它有那些辅助方法: Lists.newArrayList()Maps.newHashMap() - Joachim Sauer
1
公平的说。这可能不是直接回答问题,但由于它是原则的普遍应用,值得一提。 - Joachim Sauer
截至2014年,上述大部分建议已经过时,因为Java 7引入了钻石语法并取消了静态工厂方法的需要。 - Michael D. Moffitt

8
语法如下:
public static <T> List<T> getIListImpl() {
    return new ArrayList<T>();
}

你很接近了。
而且使用方法更像是:

MyClass.<Integer>getIListImpl();

当我尝试使用这个时,我会得到一个编译器错误:'Syntax error on token "(", Expression expected after this token'。代码:List<Integer> list = GetIListImpl<Integer>();我做错了什么? - Nick Heiner
错误一定是在别的地方。你能把你的代码放到 Pastebin 上吗? - Colin Hebert

1

首先,您必须在方法级别而不是类级别上指定通用类型。

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

看一下泛型类型参数的位置,它位于方法名之前。对于类来说,它位于类名之后。因此,你不应该在方法名后添加泛型类型,而是像这样调用它:

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
@Noel M 已修复。我倾向于将函数用于方法和函数两种情况。 - josefx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接