何时使用参数化方法调用?

23

Java方法调用可以像以下代码一样进行参数化:

class Test
{
    <T> void test()
    {
    }

    public static void main(String[] args)
    {
        new Test().<Object>test();
        //         ^^^^^^^^
    }
}

我从Eclipse Java Formatter设置对话框中发现这是有可能的,想知道是否有任何情况下这是有用或必需的。


编辑

基于Arne出色的答案,我得出了以下结论:

除了像Arne的示例所示的改进的类型安全性之外,参数化方法调用使您能够指定方法参数的公共基础类型,该类型应为容器元素的类型。通常情况下,编译器会自动推断此类型为最具体的公共基础类型。通过参数化方法调用,可以覆盖此行为。如果编译器推断出多个公共类型,则可能需要使用参数化方法调用。

以下示例演示了该行为:

import java.util.Arrays;
import java.util.List;

class Test
{
    public static void main(String[] args)
    {
        Integer a=new Integer(0);
        Long    b=new Long(0);
        List<Object> listError=Arrays.asList(a, b);
        //error because Number&Comparable<?> is not Object
        List<Object> listObj=Arrays.<Object>asList(a, b);
        List<Number> listNum=Arrays.<Number>asList(a, b);
        List<Comparable<?>> listCmp=Arrays.<Comparable<?>>asList(a, b);
    }
}

这种行为在Java语言规范第三版的第8.4.4和15.12.2.7段中有定义,但不易理解。


只有我觉得这个帖子里的每个人都认为“参数化方法”必须是一个通用方法吗?声明了参数的方法就是“参数化”的方法。通用方法只是另一种类型的参数化方法,其中类型是其中之一的参数。或者我错过了什么吗? - Martin Andersson
你说得很对!回想起来,标题可能应该是“什么时候使用无法推断类型参数的方法调用有用?” - Johann-Christoph Jacob
4个回答

22

我从未在实践中使用过这个,但你可以想象将其用于类型安全。考虑下面的方法:

<T> void method(T... items) {
    List<T> list = new ArrayList<T>();
    for (T item : items)
        list.add(item);
    System.out.println(list);
}

您可以这样调用:
o.<Object>method("Blah", new Long(0));
o.<Number>method(new Integer(100), new Long(0));

但是这会导致编译器错误:
o.<Number>method("String", new Long(0));

所以您拥有一个通用方法,它是类型安全的,可以用于任何对象,不仅限于特定的接口或类。


14

参数化方法调用在您希望允许不同类型而无需强制转换时非常有用。例如,Collections 帮助类广泛使用参数化方法调用。当您想要使用其中一个辅助方法创建一个新的通用集合时,以下是一些示例:

List<String> anEmptyStringList = Collections.<String>emptyList();
Set<Integer> unmodifiableCopy = Collections.<Integer>unmodifiableSet(originalSet);

因此,当您希望能够在其他地方使用通用类型时,您需要使用这些方法调用。它们可以防止在使用泛型时出现编译器警告。


3
变量声明不是已经推断出了集合类型吗? - Johann-Christoph Jacob
1
我在我的设置中无法重现那个警告,即使使用-Xlint参数也不行。这可能是由于编译器的差异造成的。我在Linux上使用来自Sun JDK的javac 1.6.0_20。 - Johann-Christoph Jacob
值得注意的是,在返回类型为List<String>的方法中,return collections.emptyList()是有效的,因为编译器知道你想要返回的类型,但在return list != null ? list : Collections.emptyList();中会丢失类型信息而无法编译。然而,使用参数化调用可以解决这个问题,而不需要进行强制转换,并且仍然可以使用内联语句return list != null ? list : Collections.<String>emptyList(); - user1084563
1
请注意,@user1084563所提到的问题已在Java 8中得到修复(通过openjdk-8-jdk-headless_8u111-b14-2~bpo8+1_amd64.deb和zulu8.19.0.1-jdk8.0.112-win_x64.zip进行了确认)。 - Johann-Christoph Jacob
@marcolopes,请用一个例子详细说明一下。 - Johann-Christoph Jacob
显示剩余5条评论

11

当你需要获取某个类型的集合中的一部分时,使用它可能会非常有用。

<T> List<T> filter(Collection<? extends T> coll, Predicate<? super T> pred) {
    List<T> returnList = new ArrayList<T>();
    for(T t : coll) {
        if(pred.matches(t)){
            returnList.add(t);
        }
    }
    return returnList;
}

编辑:

更普遍地说,当您希望返回特定类型或以一般方式链接两个或多个参数的类型时,这非常有用。


3
你的代码中没有任何参数化方法调用。 - newacct

1
例如,当您需要一些用于比较的通用方法时:
public static <T extends Comparable> T max(T one, T two) {
    if (one.compareTo(two) > 0) {
        return one;
    } else {
        return two;
    }
}

1
假设该方法是Aggregator类的成员,并且在声明中添加了泛型类型参数T以实现Comparable接口,那么我可以简单地调用它: Aggregator.max(new Integer(5), new Integer(10)); 调用时不需要任何参数。 - Johann-Christoph Jacob
你在那段代码中没有进行参数化方法调用。 - newacct
没错,同意。不需要额外指定类。 - Aleksejs Mjaliks

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