当前位置: 首页 > 知识库问答 >
问题:

为什么Java需要一个参数来正确推断泛型?

郭瀚海
2023-03-14

假设我们有以下代码:

public class Main {
    public static void main(String[] args) {

    }

    private static <K, V extends Comparable<V>> Map<K, V> sorted(Map<K, V> map) {
        return map.entrySet()
                .stream()
                .sorted(Comparator.comparing(Entry::getValue))
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    }
}

一个相当简单的片段——我们获取一个地图,并返回另一个代表相同地图但根据其值排序的地图。

让我们稍微改变一下功能——与其基于参数返回一些内容,不如介绍一个static对象

public class Main {
    public static void main(String[] args) {

    }

    private static Map<Integer, String> map = new HashMap<>();

    private static <K, V extends Comparable<V>> Map<K, V> sorted() {
        return map.entrySet()
                .stream()
                .sorted(Comparator.comparing(Entry::getValue))
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
    }
}

请注意,我们唯一改变的是去掉参数,取而代之的是使用静态字段。有人可能想知道为什么要引入泛型KV——毕竟它们总是分别是整数字符串。但是这难道不意味着Java可以很容易地推断出这些参数吗?为什么它会失败,错误为:

Error:(16, 25) java: incompatible types: inference variable K has incompatible bounds
equality constraints: K,K
lower bounds: java.lang.Integer

我看到了一些关于类似错误消息的问题,但我无法从中获得任何信息。这到底为什么会失败?也许值得一提的是,当编译器产生上述错误时,当使用IntelliJ IDEA时,Entry::getKeyEntry::getValue方法引用以红色高亮显示,当鼠标悬停在上方时,会显示以下消息:

Non-static method cannot be referenced from a static context.

这很奇怪,因为我在javac的实际错误消息中看不到类似的东西。

共有1个答案

於宾白
2023-03-14

具体类型参数在调用站点确定。

例如,其中任何一个都是合法通话:

Map<Void, Integer> mapA = Main.sorted();
Map<String, String> mapB = Main.sorted();
Map<Integer, GregorianCalendar> mapC = Main.sorted();

...但是,给定方法定义,它们都将返回相同的值;由于它们的类型不同,至少有两种mapA。。C的类型不正确(事实上,这三种类型都不正确)。

泛型的唯一目的是允许您省略显式类型转换;这是有利的,因为编译器能够比您更好地推理一个强制转换(或者,更确切地说,整个代码中的所有强制转换)是否安全。

所以,如果你写了一个方法,它会返回一个映射

到目前为止,我只讨论了具体的类型参数:Void、整数、字符串、格里高利日历。但是编译器没有区分这些类型参数和类型变量。因此,如果你说一个方法返回一个Map

这里重要的一点是,这必须适用于任何K和任何V,因为正如问题的第一行所述,KV的具体值是在通话地点确定的。

只有少数K可以安全地将整数转换为(整数数字对象可序列化);null整数可以转换为任何其他类型,但编译器不知道Integers是否为null。所以编译器只是说“我不能确定这些强制转换是否安全,所以我将不允许它们”。

(类似的情况适用于V)。

KV分别是整数String时,您的方法只返回一个安全值,因为map是一个Map

private static Map<Integer, String> sorted() {
    return map.entrySet()
            .stream()
            .sorted(Comparator.comparing(Entry::getValue))
            .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new));
}

(也可以是地图。)

 类似资料:
  • 为什么需要泛型 前言 泛型程序最早出现1970年代的CLU和Ada语言中, 后来被许多机遇对象和面向对象的语言锁采用 1993年C++在3.0版本中引入的模板技术就属于泛型编程 1994年7月ANSI/ISO C++标准委员会通过的STL更是泛型编程的集大成者, 它已被纳入1998年9月C++标准之中. 2004年9月Java在J2SE 5.0(JDK 1.5)中开始使用泛型技术; 2005年11

  • 问题内容: 在下面的示例中,为什么编译器能够为in中的第一次调用推断出通用参数,而在第二次调用中却无法推断出通用参数?我正在使用Java 6。 (编译错误为 Nonsense.Bar类型的func(Nonsense.Foo)方法不适用于参数(Nonsense.Foo) )。 注意:我了解编译器错误可以通过test()中的第三行来解决-我很好奇是否存在阻止编译器推断类型的特定限制。这 似乎 对我有足

  • 问题内容: 我正在尝试在我的角度应用程序中编写一个小型服务,这将使我能够选择在全局Javascript对象中指定的配置参数。除非文档准备好,否则我不想尝试访问全局配置对象(因为我不能保证脚本元素在HTML中的插入顺序)。 但是,我不明白为什么我需要调用才能使分辨率实际传播到回调。 问题答案: 在AngularJS中,resolve()的结果在$digest周期内异步传播,而不是立即传播。这意味着仅

  • 本文向大家介绍为什么我们需要一个数据库,包括了为什么我们需要一个数据库的使用技巧和注意事项,需要的朋友参考一下 数据库是数据的集合,通常以电子形式存储。数据库的设计通常是为了使其易于存储和访问信息。 好的数据库对任何公司或组织都至关重要。这是因为数据库存储了有关公司的所有相关详细信息,例如员工记录,交易记录,工资详细信息等。 数据库重要的各种原因是- 管理大量数据 数据库每天存储和管理大量数据。使

  • 问题内容: 我可以将数据发送到服务器,但是只有在使用FromBody-Attribute时才可以。 为什么无法使用Post从主体自动读取json数据? 后端Web API 前端angularjs 问题答案: 仅因为某事是POST请求,所以没有明确的规则如何传递参数。POST请求仍可以包含URL中编码的查询参数。方法参数应该是“简单”类型(字符串,整数等)的查询参数。 通常,复杂类型应该是POST表

  • 我试图理解为什么我们需要通配符——Java泛型中的问号,为什么我们不能使用普通的单字符t或E等作为类型?请看以下示例: 结果是一样的,尽管通配符版本更简洁。这是唯一的好处吗?