推断嵌套静态泛型函数的泛型类型

发布于 11-14 09:25 字数 2553 浏览 6 评论 0原文

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 来自 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;
}

Is the Java compiler able to infer the type of a generic static function from its context as the argument to another generic static function?

For example, I have a simple Pair class:

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;
    }

    // ...
}

And I have the following generic static function:

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();
    }
};

Which I wish to use as follows (Collections2.transform is from Google Guava):

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

To which the compiler complains:

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>)

So it seems that the compiler fails to propagate the types inferred for transform() to deferredFirst() as it thinks they are Objects.

Forcing the compiler to understand the types in either of these ways works:

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());

Is it possible to change either function's signature to allow the compiler to infer/propagate the types?

Edit: For Bohemian, here's a possible method the above example could be used in:

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;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

嘿哥们儿2024-11-21 09:25:57

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

static <T> T foo();

String s = foo();

print( foo() )

在赋值上下文中,程序员的意图很明确,T应该是String

在下一行,而不是那么多。

print 方法不是一个非常公平的例子,它严重重载。假设print没有被重载,它的参数类型是固定的,因此可以清楚地推断出T。编译器不应该足够聪明来解决这个问题吗?

这听起来很合理,直到有人冒险阅读相关规范文本, 15.12 方法调用表达式 祝你好运,改变混乱中的任何东西!

它太复杂了,连编译器作者都看不懂。 javac 和其他编译器中存在大量源于规范这一部分的错误。

Type inference is nasty and complicated. They have to stop somewhere. Consider

static <T> T foo();

String s = foo();

print( foo() )

In the assignment context, the intention of the programmer is clear, T should be String

In the next line, not so much.

The print method is not a very fair example, it is heavily overloaded. Suppose print isn't overloaded, its parameter type is fixed, so T can be clearly inferred. Shouldn't the compiler be smart enough to figure it out?

That sounds reasonable, until one ventures to read the related spec text, 15.12 Method Invocation Expressions Good luck changing anything in that mess!

It is so complicated, not even the compiler authors understand it. There are tons of bugs in javac and other compilers that originated from this section of the spec.

君勿笑2024-11-21 09:25:57

试试这个泛型功夫:

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.staticMethod() 语法键入静态方法。

Try this generics kung fu:

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;
}

You can type the method call and pass the generics through to the next call.

I'm not sure about the exact generic parameters to use here, because you haven't included enough code. If you paste in the whole method where the problem is I will edit this answer to make it compile. EDITED: with new info from question

Please let me know if it compiles. If it isn't that solution, it will be close. The key is to type the static method using Class.<Type>staticMethod() syntax.

下壹個目標2024-11-21 09:25:57

我不久前想到的是:

@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() 提供通用参数。我认为这是你能得到的最好的。

What I came up with a while ago is:

@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;
}

Don't let the "SuppressWarnings" put you off, it works fine.

Example:

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());

Unfortunately, yes, you have to supply the generic argument to extractFirst(). I think this is the best you'll get though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文