2011-06-07 128 views
11

Java编译器是否能够从其上下文中推断泛型静态函数的类型作为另一个泛型静态函数的参数?推测嵌套静态泛型函数的泛型类型

例如,我有一个简单的Pair类:

public class Pair<F, S> { 

    private final F mFirst; 

    private final S mSecond; 

    public Pair(F first, S second) { 
     mFirst = checkNotNull(first); 
     mSecond = checkNotNull(second); 
    } 

    public static <F, S, F1 extends F, S1 extends S> Pair<F, S> of(F1 first, S1 second) { 
     return new Pair<F, S>(first, second); 
    } 

    public F first() { 
     return mFirst; 
    } 

    public S second() { 
     return mSecond; 
    } 

    // ... 
} 

而且我有下面的一般静态函数:

public static <F, P extends Pair<F, ?>> Function<P, F> deferredFirst() { 
    return (Function<P, F>)DEFERRED_FIRST; 
} 

private static final Function<Pair<Object, ?>, Object> DEFERRED_FIRST = 
     new Function<Pair<Object,?>, Object>() { 

    @Override 
    public Object apply(Pair<Object, ?> input) { 
     return input.first(); 
    } 
}; 

哪我希望用如下(Collections2.transform is from Google Guava):

List<Pair<Integer, Double>> values = ... 
Collection<Integer> firsts = Collections2.transform(values, 
     Pair.deferredFirst()); 

编译器抱怨到:

The method transform(Collection<F>, Function<? super F,T>) in the type 
Collections2 is not applicable for the arguments 
(List<Pair<Integer,Double>>, Function<Pair<Object,?>,Object>) 

因此,编译器似乎无法将transform()推断的类型传播给deferredFirst(),因为它认为它们是对象。

强迫编译器了解类型通过以下两种方式运作:

Function<Pair<Integer, ?>, Integer> func = Pair.deferredFirst(); 
Collection<Integer> firsts = Collections2.transform(values, func); 


Collection<Integer> firsts = Collections2.transform(values, 
     Pair.<Integer, Pair<Integer, ?>>deferredFirst()); 

是否有可能改变任何一个函数的签名,让编译器推断/传播的类型?

编辑:对于波西米亚,这里是一个可能的方法上面的例子可以用于:

public static int sumSomeInts(List<Pair<Integer, Double>> values) { 
    Collection<Integer> ints = Collections2.transform(values, 
      Pair.deferredFirst()); 
    int sum = 0; 
    for(int i : ints) 
     sum += i; 
    return sum; 
} 
+2

要小心,当你有'A级 {公共无效方法(){}}'。我的猜测是,'楼S'的方法来覆盖从你的类{){}公共无效方法(}' – toto2 2011-06-07 12:57:20

回答

4

类型推断是讨厌和复杂的。他们必须在某个地方停下来。考虑

static <T> T foo(); 

String s = foo(); 

print(foo()) 

在分配方面,程序员的意图很明显,TString

下一行,没有这么多。

print方法不是一个很好的例子,它是沉重的重载。假设print没有超载,其参数类型是固定的,所以T可以清楚地推断出来。编译器是否应该足够聪明以解决问题?

这听起来很合理,直到有人冒险阅读相关规范文本,15.12 Method Invocation Expressions好运气改变了那个烂摊子里的东西!

它太复杂了,即使编译器作者也不理解它。 javac和其他编译器中有大量的bug来源于这部分规范。

+0

谢谢,很高兴有一些背景信息 – Hal 2011-06-07 14:54:29

1

试试这个仿制药的功夫:

public static int sumSomeInts(List<Pair<Integer, Double>> values) { 
    Collection<Integer> ints = Collections2.transform(values, 
     Pair.<Integer, Double>deferredFirst()); 
    int sum = 0; 
    for(int i : ints) 
     sum += i; 
    return sum; 
} 

可以键入方法调用和将泛型传递给下一个呼叫。

我不确定这里使用的确切泛型参数,因为您没有包含足够的代码。如果你粘贴问题的整个方法,我将编辑这个答案,使其编译。 编辑:从问题的新信息

请让我知道它是否编译。如果不是这个解决方案,它会很接近。关键是使用Class.<Type>staticMethod()语法键入静态方法。

+0

我用一个简单的例子方法编辑。 – Hal 2011-06-07 13:15:09

+0

的那些(即相当于'A级感谢您的帮助,因为没有明确的添加类型到嵌套的调用没有解决方案我已经接受了不可信的答案,因为它有更多关于这个问题的一般信息 – Hal 2011-06-07 14:48:27

1

我就怎么了前一段时间是:

@SuppressWarnings("rawtypes") 
private static final Function ExtractFirst = new Function() { 
    @Override 
    public Object apply(final Object from) { 
     Preconditions.checkNotNull(from); 
     return ((Pair)from).first; 
    } 
}; 

@SuppressWarnings("unchecked") 
public static <A> Function<Pair<A,?>,A> extractFirst() { 
    return ExtractFirst; 
} 

不要让“SuppressWarnings”把你赶走,它工作正常。

例子:

List<Pair<String,String>> pairs = ImmutableList.of(Pair.of("a", "b"), 
    Pair.of("c", "d"),Pair.of("e", "f")); 
Iterable<String> firsts = Iterables.transform(pairs, 
    Pair.<String>extractFirst()); 

可惜的是,你必须在一般的参数提供给extractFirst()。我认为这是你得到的最好的。

+0

And 'firsts'会是[“a”,“c”,“e”],对不对? – Ray 2011-06-09 18:30:04