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

为什么返回类型的泛型在Java8中存在未检查的方法参数转换时被擦除?

盖锦程
2023-03-14

考虑下面的代码示例:

import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List list = new ArrayList<Integer>();
        String response = getProducer(list).get();
    }
    static Producer<String> getProducer(List<Integer> list) {
        return new Producer<String>();
    }
}
class Producer<T> {
    T get() {
        return null;
    }
}

用Java编译时

警告:(7,39)java:需要未经检查的转换:java。util。列表

但是,当用Java编译时

错误:(7,48)java:不兼容的类型:java。lang.Object无法转换为java。lang.String

显然,从getProducer(list)返回的类型不是Producer

奇怪的是,在调用getProducer方法时,可以通过以下方式避免未经检查的转换来修复此问题:

  • 列表更改getProducer的参数类型
  • 使用的Java是Oracle JDK 1.8.040
  • 当返回值的泛型类型在方法签名中固定时,传递参数的泛型类型如何影响方法返回值的泛型类型
  • 为什么Java和Java之间的行为会有如此向后不兼容的变化

共有3个答案

郗缪文
2023-03-14

真正的问题是;为什么你可以使用原始类型?向后兼容。如果是为了向后兼容,则假设仅应使用原始类型。

当返回值的泛型类型在方法签名中固定时,传递参数的泛型类型如何影响方法返回值的泛型类型?

方法或构造函数有两种模式中的一种。它要么是完全通用的,要么是完全原始类型的,以实现向后兼容。不存在部分原始类型和部分通用的使用模式。原始类型是选项的唯一原因是向后兼容,在这种情况下,它假设所有类型都是/曾经是原始类型。

为什么Java7和Java8之间的行为发生了如此向后不兼容的变化?

由于Java1.4不再被支持,并且已经有一段时间没有被支持了,向后兼容性参数不太成立,在当前语言中给原始类型一个位置是有意义的。

陆宏扬
2023-03-14

让我们看看Java语言规范:

Java

>

  • 如果所选方法不是泛型方法,则:

    • 如果要使该方法适用,必须进行未经检查的转换,则调用类型的参数类型是该方法类型的参数类型,而返回类型和抛出类型是通过删除该方法类型的返回类型和抛出类型而给出的

    Java

    所选方法的结果类型确定如下:

    >

    否则,如果要使方法适用,必须进行未经检查的转换,则结果类型为方法声明的返回类型的擦除(§4.6)。

    理解“通用方法”的含义很重要:

    参考文献

    如果方法声明了一个或多个类型变量(§4.4),则该方法是泛型的。

    换句话说,方法

    static Producer<String> getProducer(List<Integer> list)
    

    具有泛型参数和返回类型,但不是泛型的,因为它不声明类型变量。

    因此,引用的部分适用,尽管在先决条件方面有所不同,但他们同意这个特定方法调用的后果,“如果未经检查的转换是方法适用所必需的,那么结果类型是擦除...方法声明的返回类型”。

    因此,旧的编译器使用返回类型Producer违反了规范

  • 钦良弼
    2023-03-14

    这看起来像是此处和此处报告的已知兼容性问题。

    从第二个链接:

    以下在JDK 7中编译并带有警告的代码将不会在JDK 8中编译:

    import java.util.List;
    class SampleClass {
    
         static class Baz<T> {
             public static List<Baz<Object>> sampleMethod(Baz<Object> param) {
                 return null;
             }
         }
    
         private static void bar(Baz arg) {
             Baz element = Baz.sampleMethod(arg).get(0);
         }
    }
    

    在JDK 8中编译这段代码会产生以下错误:

    SampleClass.java:12:错误:不兼容类型:对象无法转换为Baz

    Baz element = Baz.sampleMethod(arg).get(0);
    

    注:样本类别。java使用未经检查或不安全的操作。注意:使用-Xlint重新编译:未选中以获取详细信息。1错误

    由此推导,OP的代码可以通过替换这一行来修复(右手边的类型声明让我大吃一惊——我把它看作一个类型化数组列表,但它不是):

    List list = new ArrayList<Integer>();
    

    具有

    List<Integer> list = new ArrayList<Integer>();
    

    这不会导致类型从返回类型的方法getProducer(List

    再次引用第二链接:

    在本例中,一个原始类型被传递给samplemethod(Baz

     类似资料:
    • 问题内容: 考虑以下代码示例: 当用Java 7编译时,它只会产生预期的警告: 警告:(7,39)Java:要求进行未经检查的转换: 找到: 但是,在Java 8中进行编译时,它会为分配产生以下错误: 错误:(7,48)Java:不兼容的类型:无法转换为 因此,显然从返回的类型不是,而是被擦除(也可以通过IDE中的“提取变量”功能进行确认)。这很令人困惑,因为方法总是返回。 奇怪的是,可以通过在调

    • 下面对getHighest()和getLowest()的调用返回Comparable类型的对象,而不是T类型的对象,这正是我们想要的。为什么,我该如何改进这段代码,使这些调用返回T(这样T的字段和方法就可用了)? 下一行生成编译器错误: 错误:找不到符号符号:方法getName()位置:接口java.lang.Comparable 我想employee.getHighest()返回一个员工(而不仅

    • 我有一个返回泛型的函数: 所以,当我试图匹配一个函数结果时,我的问题是: 我得到一个警告:“类型模式数组[CustomerInfo到[CustomerApplication到]]中的非变量类型参数CustomerApplication-DDTO未选中,因为它已被擦除消除。” 这是否意味着在Array[]中可以得到任何类型的数组?所以我已经阅读了关于ClassTag和TypeTag的文章,但是误解了

    • 嗨,我在泛型类型和从IDE得到的警告方面有问题。我有一个保存泛型类型值的类: 我有一个抽象类: 以及扩展它的类: IDE警告我:未经检查的重写返回类型需要未经检查的转换。我错过了什么?

    • 问题内容: 我是从Joshua Bloch的google I / O困惑者演讲中得到的。这是代码 这个main方法会引发异常,因为它是原始类型,因此in中的所有泛型都将被删除,因此最终调用而不是。 我的问题是,即使我打电话是不是应该把它调用的方法,因为由于类型擦除,这种方法是有效的并且是类型的不是? 问题答案: 被调用的方法是在编译时定义的,而不是在运行时定义的。 如果在构造函数调用中添加参数,则

    • 问题总结:我想传递一个带有类型参数(如