繁体   English   中英

Java何时需要显式类型参数?

[英]When does Java require explicit type parameters?

鉴于:

import com.google.common.collect.ImmutableMap;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Stream;

public class Testcase
{
    public static <T, K, V> MapCollectorBuilder<T, K, V>
        toImmutableMap(Function<? super T, ? extends K> keyMapper,
            Function<? super T, ? extends V> valueMapper)
    {
        return null;
    }

    public static final class MapCollectorBuilder<T, K, V>
    {
        public Collector<T, ?, ImmutableMap<K, V>> build()
        {
            return null;
        }
    }

    public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toImmutableMap2(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper)
    {
        return null;
    }

    public void main(String[] args)
    {
        Function<String, String> keyMapper = i -> i;
        Function<String, Integer> valueMapper = Integer::valueOf;

        ImmutableMap<String, Integer> map1 = Stream.of("1", "2", "3")
            .collect(Testcase.toImmutableMap(keyMapper, valueMapper).build());

        ImmutableMap<String, Integer> map2 = Stream.of("1", "2", "3")
            .collect(Testcase.toImmutableMap(i -> i, Integer::valueOf).build());

        ImmutableMap<String, Integer> map3 = Stream.of("1", "2", "3")
            .collect(Testcase.toImmutableMap2(i -> i, Integer::valueOf));
    }
}

涉及map1map3的语句编译正常,但map2失败:

Testcase.java:[41,57] incompatible types: cannot infer type-variable(s) T,K,V
    (argument mismatch; invalid method reference
      no suitable method found for valueOf(java.lang.Object)
          method java.lang.Integer.valueOf(java.lang.String) is not applicable
            (argument mismatch; java.lang.Object cannot be converted to java.lang.String)
          method java.lang.Integer.valueOf(int) is not applicable
            (argument mismatch; java.lang.Object cannot be converted to int))

可以通过提供显式类型参数<String, String, Integer>来解决编译器错误。

  1. Java 8何时需要显式类型参数? 意思是,是否存在打破类型推断的已知模式?
  2. 可以toImmutableMap()MapCollectorBuilder以避免使用显式类型参数而不会丢失使用Builder来配置收集器吗?

更新

  1. 为什么涉及map3的语句有效? 它与涉及map2的语句有何不同?

回答你的问题“意义,是否有一种打破类型推断的已知模式?”很快:当然,有一种模式,而且对Java编程语言的整个行为有一个巨大的规范

但是关于类型推断和方法调用类型的章节实际上是详尽无遗且难以理解的。 这可以通过以下事实得到最好的说明:在出现意外行为的情况下,通常会根据规范进行关于预期行为的大型讨论。

但对于程序员来说,有一些问题可以解释并且可以记住。

有两种方法可以推断类型参数,通过传递给方法的参数或构成表达式的部分,或者通过表达式的目标类型 ,即调用参数的期望类型,变量在返回语句的情况下分配的方法或方法的返回类型。

目标类型可以通过嵌套方法调用传播,如in

TargetType x=foo(bar(/*target type can be used*/));

或者像条件一样

TargetType x=condition? foo(/*target type can be used*/): foo(/*target type can be used*/);

不是在链接调用的情况下

TargetType x=foo(/*target type can NOT be used*/).foo();

现在举例来说:

ImmutableMap<String, Integer> map1 = Stream.of("1", "2", "3").collect( expression );

这里, Stream.of(…).collect(…)链接的,因此目标类型不能用于确定的流类型of调用,但提供给该方法的参数是足够的,以推断出类型Stream<String> collect方法提供了分配给map1的结果,因此流类型Stream<String> 目标类型ImmutableMap<String, Integer>都是已知的,可用于表达式的类型推断。 在表达式上:

  • Testcase.toImmutableMap(keyMapper, valueMapper).build()这是一个链式调用,因此目标类型对于build()是已知的,但对于toImmutableMap 但是, toImmutableMap的参数是具有已知确切类型的局部变量,因此类型推断可以使用它们来推断toImmutableMap的结果类型,并检查它是否与toImmutableMap .build()的期望相匹配

  • Testcase.toImmutableMap(i -> i, Integer::valueOf).build()这又是一个链式调用,但现在参数i - > i的类型不完整,并且缺少目标类型。 在不知道目标类型的情况下尝试猜测i -> i的类型失败。

  • Testcase.toImmutableMap2(i -> i, Integer::valueOf)不是链式调用,因此目标类型可用于toImmutableMap2调用(关于collect调用,它是嵌套调用)。 因此, toImmutableMap2的目标类型允许推断参数的目标类型,因此对于i -> i lambda表达式。 使用适当的目标类型,可以推断出正确的功能签名。

lambda表达式的目标类型完全取决于上下文, 如Java教程中所述 因此,lambdas不会对类型参数推断做出贡献; 相反,他们依赖它。 方法引用“ 已经有名称的方法的紧凑,易于阅读的lambda表达式 ”(Oracle Java教程;强调添加),因此没有区别,当它们涉及时,颜色类型分析会有不同的颜色。

将lambda /方法引用分配给变量时,该变量的类型提供了用于推断类型参数的上下文。 但是,当您将它们直接传递给泛型方法时,您需要一些其他机制来推断它们的类型。 在某些情况下,该方法的其他参数可能会用于此目的。 在您的特定情况下,您可能需要显式类型参数:

ImmutableMap<String, Integer> map2 = Stream.of("1", "2", "3").collect(
        Testcase.<String, String, Integer>toImmutableMap(i -> i, Integer::valueOf).build());

更新

关于更新的问题,看起来Java可以在map3案例中正确推断类型,部分原因是因为调用MapCollectorBuilder.build()方法并不复杂。 如果没有build()map3的类型提供了上下文来确定Stream.collect()的第一个类型参数,它给出了KV 类型参数T可以从Stream的(推断的)类型推断出来。

但是,在涉及build()情况下,我认为Java正在将通用方法的类型参数推断为toImmutableMap()的问题与其返回值上调用build()的返回值的类型问题toImmutableMap() 换句话说,它需要在考虑通过对该值调用方法获得的值的类型之前确定由toImmutableMap()返回的对象的类型。

还有另一种解决方法。 您可以给编译器明确指定标识lambda的参数类型提示:

ImmutableMap<String, Integer> map2 = Stream.of("1", "2", "3")
    .collect(Testcase.toImmutableMap((String i) -> i, Integer::valueOf).build());

在Javac 1.8.0_25和ECJ 3.10.2中编译正常。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM