当前位置: 首页 > 面试题库 >

如果在Java 8中对方法参数进行未经检查的转换,为什么会删除返回类型的泛型?

通寂离
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编译时,它只会产生预期的警告getProducer(list)

警告:(7,39)Java:要求进行未经检查的转换:java.util.List<java.lang.Integer> 找到:
java.util.List

但是,在Java 8中进行编译时,它会为response = getProducer(list).get()分配产生以下错误:

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

因此,显然从返回的类型getProducer(list)不是Producer<String>,而是被擦除Producer(也可以通过IDE中的“提取变量”功能进行确认)。这很令人困惑,因为getProducer方法总是返回Producer<String>

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

  • 将参数类型getProducer从更改List<Integer>List
  • list变量类型从更改ListList<Integer>

更新

  • 使用的Java是Oracle JDK 1.8.0_40
  • 我也尝试过使用Java 8编译器从1.5到1.7的源和目标选项,结果是相同的。

问题

  • 在方法签名中固定 返回值 的通用类型的同时,传递的 参数 的通用类型如何影响方法 返回值 的通用类型?
  • 为什么Java 7和Java 8之间的行为有这种向后不兼容的变化?

问题答案:

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

从第二个链接:

以下在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);

注意:SampleClass.java使用未经检查或不安全的操作。注意:使用-Xlint:unchecked重新编译以获取详细信息。1个错误

因此,可以通过替换此行来固定OP的代码(右侧的类型声明使我失望-我将其视为不是类型化数组列表的形式阅读):

List list = new ArrayList<Integer>();

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

这不会导致类型从方法的返回类型中删除 getProducer(List<Integer> list)

再次引用第二个链接:

在此示例中,将原始类型传递给sampleMethod(Baz<Object>)可通过子类型化应用的 方法(请参见JLS,Java SE 7
Edition,第15.12.2.2节)。要应用该方法,必须进行未经检查的转换,因此将删除其返回类型(请参见JLS,Java SE 7
Edition,第15.12.2.6节)。在此情况下的返回类型sampleMethod(Baz<Object>)
java.util.List代替java.util.List<Baz<Object>>并因此返回类型get(int)Object,这是不分配兼容Baz



 类似资料:
  • 考虑下面的代码示例: 用Java编译时 警告:(7,39)java:需要未经检查的转换:

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

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

  • 我正在堆栈中实现一个列表链接。该程序成功通过了所有必需的测试,但我遇到了此警告。程序的实现没有中断,但我正在寻求消除此问题。你能告诉我如何通过编辑Equals方法中的代码来修复它吗?未选中的强制转换:“java.lang.对象”到“LinkedStack”

  • 问题内容: 考虑以下代码: 如果我用以下命令编译它: 它返回: 请注意,即使在返回类型上未引用任何泛型类型,也仅将泛型方法视为不安全。 这是一个错误吗?还是有一个更深层次的原因我没有考虑? 问题答案: 允许使用原始类型以确保与引入泛型之前编写的代码兼容。原始类型的工作原理是,仅从所有方法参数和返回类型中忽略 所有 类型信息,甚至与该类的类型参数无关的类型信息。正如您所发现的,这可能导致奇怪的结果。

  • 我大致了解什么是类型擦除以及为什么我们会遇到未经检查的警告。但是,我不明白为什么在以下情况下只发出一个未经检查的警告: 不幸的是,两个接听电话都与大小写子句匹配。编译器确实为第一个子句发出了警告: 警告:未检查类型模式列表[Double]中的非可变类型参数Double,因为它已被擦除消除。 我知道TypeTag[T]可以用来实现更好的类型安全。但是我在这里关心的是为什么没有针对第二个case子句发