推理变量具有不兼容的界限.Java 8 编译器回归? [英] Inference variable has incompatible bounds. Java 8 Compiler Regression?

查看:37
本文介绍了推理变量具有不兼容的界限.Java 8 编译器回归?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下程序在 Java 7 和 Java 8 的 Eclipse Mars RC2 中编译:

import java.util.List;公共类测试{static final void a(Class> type) {b(newList(type));}静态最终 <T>列表b(列表列表){退货清单;}static final >L newList(Class type) {尝试 {返回 type.newInstance();}捕获(异常 e){抛出新的运行时异常(e);}}}

使用javac 1.8.0_45编译,报如下编译错误:

Test.java:6: 错误:Test 类中的方法 b 不能应用于给定类型;b(newList(type));^要求:List发现:CAP#1原因:推理变量 L 有不兼容的界限等式约束:CAP#2上限:List,List其中 T,L 是类型变量:T 扩展在方法 <T>b(List<T>) 中声明的对象L 扩展 List在方法 <L>newList(Class<L>) 中声明其中 CAP#1、CAP#2、CAP#3 是新的类型变量:CAP#1 扩展了 List从捕获?扩展列表CAP#2 扩展了 List从捕获?扩展列表CAP#3 从 ?

一种解决方法是在本地分配一个变量:

import java.util.List;公共类测试{static final void a(Class> type) {//解决方法在这里列表变量 = newList(type);b(变量);}静态最终 <T>列表b(列表列表){退货清单;}static final >L newList(Class type) {尝试 {返回 type.newInstance();}捕获(异常 e){抛出新的运行时异常(e);}}}

我知道类型推断在 Java 8 中发生了很大变化(例如由于 JEP 101通用目标类型推断").那么,这是一个错误还是一个新的语言功能"?

编辑:我也向 Oracle 报告了 JI-9021550,但以防万一这是 Java 8 中的特性",我也向 Eclipse 报告了这个问题:

感谢 错误报告,感谢 Holger,为您提供答案中的示例.这些和其他几个最终让我质疑 11 年前在 Eclipse 编译器中所做的一个小变化.重点是:Eclipse 非法扩展了捕获算法以递归应用于通配符边界.

有一个例子,这个非法更改将 Eclipse 行为与 javac 完美对齐.与我们在 JLS 中清楚地看到的相比,几代 Eclipse 开发人员更信任这个旧决定.今天我相信之前的偏差肯定有不同的原因.

今天我鼓起勇气在这方面将 ecj 与 JLS 保持一致,瞧 5 个看起来极其难以破解的错误,基本上就这样解决了(加上这里和那里的一些小调整作为补偿).

>

因此:是的,Eclipse 有一个错误,但该错误已在 4.7 里程碑 2 时得到修复:)

以下是 ecj 今后将报告的内容:

类型Test中的方法b(List)不适用于参数(capture#1-of ? extends List)

它是捕获边界内的通配符,它​​没有找到检测兼容性的规则.更准确地说,在推理过程中的某个时间(准确地说是合并),我们遇到了以下约束(T#0 表示推理变量):

⟨T#0 = ?⟩

天真地,我们可以将类型变量解析为通配符,但是——大概是因为通配符不被视为类型——减少规则将上述定义为减少到 FALSE,从而使推理失败.

The following program compiles in Java 7 and in Eclipse Mars RC2 for Java 8:

import java.util.List;

public class Test {

    static final void a(Class<? extends List<?>> type) {
        b(newList(type));
    }

    static final <T> List<T> b(List<T> list) {
        return list;
    }

    static final <L extends List<?>> L newList(Class<L> type) {
        try {
            return type.newInstance();
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

Using the javac 1.8.0_45 compiler, the following compilation error is reported:

Test.java:6: error: method b in class Test cannot be applied to given types;
        b(newList(type));
        ^
  required: List<T>
  found: CAP#1
  reason: inference variable L has incompatible bounds
    equality constraints: CAP#2
    upper bounds: List<CAP#3>,List<?>
  where T,L are type-variables:
    T extends Object declared in method <T>b(List<T>)
    L extends List<?> declared in method <L>newList(Class<L>)
  where CAP#1,CAP#2,CAP#3 are fresh type-variables:
    CAP#1 extends List<?> from capture of ? extends List<?>
    CAP#2 extends List<?> from capture of ? extends List<?>
    CAP#3 extends Object from capture of ?

A workaround is to locally assign a variable:

import java.util.List;

public class Test {

    static final void a(Class<? extends List<?>> type) {

        // Workaround here
        List<?> variable = newList(type);
        b(variable);
    }

    static final <T> List<T> b(List<T> list) {
        return list;
    }

    static final <L extends List<?>> L newList(Class<L> type) {
        try {
            return type.newInstance();
        }
        catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

I know that type inference has changed a lot in Java 8 (e.g. due to JEP 101 "generalized target-type inference"). So, is this a bug or a new language "feature"?

EDIT: I have also reported this to Oracle as JI-9021550, but just in case this is a "feature" in Java 8, I've reported the issue also to Eclipse:

解决方案

Thanks for the bug report, and thanks, Holger, for the example in your answer. These and several others finally made me question one small change made in the Eclipse compiler 11 years ago. The point was: Eclipse had illegally extended the capture algorithm to apply recursively to wildcard bounds.

There was one example where this illegal change perfectly aligned Eclipse behavior with javac. Generations of Eclipse developers have trusted this old decision more than what we could clearly see in JLS. Today I believe that previous deviation must have had a different reason.

Today I took the courage to align ecj with JLS in this regard and voila 5 bugs that appeared to be extremely hard to crack, have essentially been solved just like that (plus a little tweak here and there as compensation).

Ergo: Yes, Eclipse had a bug, but that bug has been fixed as of 4.7 milestone 2 :)

Here's what ecj will report henceforth:

The method b(List<T>) in the type Test is not applicable for the arguments (capture#1-of ? extends List<?>)

It's the wildcard inside a capture bound that doesn't find a rule to detect compatibility. More precisely, some time during inference (incorporation to be precise) we encounter the following constraint (T#0 representing an inference variable):

⟨T#0 = ?⟩

Naively, we could just resolve the type variable to the wildcard, but -- presumably because wildcards are not considered types -- the reduction rules define the above as reducing to FALSE, thus letting inference fail.

这篇关于推理变量具有不兼容的界限.Java 8 编译器回归?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆