下界通配符在javac中造成麻烦,但不是Eclipse [英] Lower-bounded wild card causes trouble in javac, but not Eclipse

查看:150
本文介绍了下界通配符在javac中造成麻烦,但不是Eclipse的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这段代码在Eclipse中编译,但不在javac中:
$ b

  import java.util .function.Consumer; 

public class Test {
public static final void m1(Consumer<?> c){
m2(c);
}
private static final< T> void m2(Consumer <?super T> c){
}
}

javac输出:


$ b

  C:\Users\lukas\workspace> javac -version 
javac 1.8.0_92

C:\Users\lukas\workspace> javac Test.java
Test.java:5:error:method m2 in class Test can not适用于给定的类型;
m2(c);
^
要求:消费者< ;?超T>
找到:消费者< CAP#1>
reason:无法推断类型变量T
(参数不匹配; Consumer< CAP#1>无法转换为Consumer< ;? super T>)
其中T是类型 - 变量:
T extends在方法< T> m2(Consumer< ;? super T>)
中声明的对象,其中CAP#1是一个新的类型变量:
CAP#1 extends Object捕获?
1错误
---------------------------------------- ------------------------

哪个编译器错了,为什么? ( Eclipse错误报告和这里讨论的部分

此代码与JLS 8是合法的。javac版本8及更早版本在处理通配符和捕获方面存在一些错误。从版本9开始(早期访问,我尝试了版本ea-113和更新版本),并且javac接受此代码。

为了理解编译器如何根据JLS分析它,对于区分通配符捕获,类型变量,推理变量等是至关重要的。


类型 c Consumer< capture#1-of?> (javac会写 Consumer< CAP#1> )。这种类型是未知的,但是是固定的。

m2 的参数的类型是 Consumer< ?超级T> ,其中 T 是一个要通过类型推断实例化的类型变量。

<在类型推断过程中,一个用ecj表示的推断变量
用于表示 T code>。

类型推断包括确定 T#0 是否可以实例化为任何类型而不违反任何给定的类型约束。在这个特殊情况下,我们从这个约束开始:


⟨c→消费者<?超级T#0>⟩


这是逐步减少的(通过应用 JLS 18.2 ):


⟨消费者<俘获#1→?→消费者< ;? super T#0>⟩



⟨捕获#1个? < =? super T#0⟩



⟨T#0 <:捕获#1-⟩



T #0 <:捕获#1


最后一行是类型绑定并且完成了缩减。由于没有进一步的约束,解决方法将 T#0 实例化为类型 capture#1-of



通过这些步骤,类型推断已经证明 m2 适用于这个特定的调用。 qed。

直观上,所示解决方案告诉我们:如果 T 设置为捕获可能表示的任何类型表示完全相同的类型,没有违反类型约束。这是可能的,因为捕获在开始类型推断之前是固定的。


This piece of code compiles in Eclipse but not in javac:

import java.util.function.Consumer;

public class Test {
    public static final void m1(Consumer<?> c) {
        m2(c);
    }
    private static final <T> void m2(Consumer<? super T> c) {
    }
}

javac output:

C:\Users\lukas\workspace>javac -version
javac 1.8.0_92

C:\Users\lukas\workspace>javac Test.java
Test.java:5: error: method m2 in class Test cannot be applied to given types;
        m2(c);
        ^
  required: Consumer<? super T>
  found: Consumer<CAP#1>
  reason: cannot infer type-variable(s) T
    (argument mismatch; Consumer<CAP#1> cannot be converted to Consumer<? super T>)
  where T is a type-variable:
    T extends Object declared in method <T>m2(Consumer<? super T>)
  where CAP#1 is a fresh type-variable:
    CAP#1 extends Object from capture of ?
1 error
----------------------------------------------------------------

Which compiler is wrong and why? (Eclipse bug report and parts of discussion here)

解决方案

This code is legal wrt JLS 8. javac version 8 and earlier had several bugs in how they handle wildcards and captures. Starting with version 9 (early access, I tried version ea-113 and newer) also javac accepts this code.

To understand how a compiler analyzes this according to JLS, it is essential to tell apart what are wildcard captures, type variables, inference variables and such.

The type of c is Consumer<capture#1-of ?> (javac would write Consumer<CAP#1>). This type is unknown, but fixed.

The parameter of m2 has type Consumer<? super T>, where T is a type variable to be instantiated by type inference.

During type inference an inference variable, denoted by ecj as T#0, is used to represent T.

Type inference consists in determining, whether T#0 can be instantiated to any type without violating any given type constraints. In this particular case we start with this contraint:

⟨c → Consumer<? super T#0>⟩

Which is stepwise reduced (by applying JLS 18.2):

⟨Consumer<capture#1-of ?> → Consumer<? super T#0>⟩

⟨capture#1-of ? <= ? super T#0⟩

⟨T#0 <: capture#1-of ?⟩

T#0 <: capture#1-of ?

The last line is a "type bound" and reduction is done. Since no further constraints are involved, resolution trivially instantiates T#0 to the type capture#1-of ?.

By these steps, type inference has proven that m2 is applicable for this particular invocation. qed.

Intuitively, the shown solution tells us: whatever type the capture may represent, if T is set to represent the exact same type, no type constraints are violated. This is possible, because the capture is fixed before starting type inference.

这篇关于下界通配符在javac中造成麻烦,但不是Eclipse的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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