【问题标题】:Java type error when compiling code with javac 8 which worked fine with javac 6使用 javac 8 编译代码时出现 Java 类型错误,该代码在 javac 6 中运行良好
【发布时间】:2016-10-27 19:36:58
【问题描述】:

我有这个类,它是我在从 Java 6 移植到 Java 8 的项目中找到的一些代码的简化:

public class Unification {

    final class Box<A> {}

    final class MyMap<A, B extends Box<? extends A>> {}

    MyMap<?, ?> getMap() {
        return new MyMap<Object, Box<Object>>();
    }

    <A, B extends Box<? extends A>> void setMap(final MyMap<A, B> m) {}

    void compileError() {
        setMap(getMap());
    }

}

这是一个非常小的示例,仅用于展示问题,实际代码更有意义。这个问题似乎很笼统,因此是抽象的例子。核心问题如下:出于某种原因,javac 不希望接受 MyMap&lt;?, ?&gt; 类型的表达式作为 setMap() 方法的参数,尽管根据我的理解,这应该是良好的类型。

使用 javac 6 编译代码没有错误,但是当我使用 javac 8 时收到此模糊错误消息:

C:\System9\KWS_sparse\sourcesNG\Domain\src\uz\Unification.java (21:9) error: method setMap in class Unification cannot be applied to given types;
required: Unification.MyMap<A,B>
found: Unification.MyMap<CAP#1,CAP#2>
reason: inference variable A has incompatible bounds
equality constraints: CAP#1
lower bounds: Object
where A,B are type-variables:
A extends Object declared in method <A,B>setMap(Unification.MyMap<A,B>)
B extends Unification.Box<? extends A> declared in method <A,B>setMap(Unification.MyMap<A,B>)
where CAP#1,CAP#2 are fresh type-variables:
CAP#1 extends Object from capture of ?
CAP#2 extends Unification.Box<? extends CAP#1> from capture of ?

报错信息似乎提示MyMap的第一个类型参数找不到统一,javac找不到和CAP#1都统一的类型,这表示@的实参中的第一个通配符参数987654328@,和A,是setMap()的形参对应的类型参数。尽管在我看来 ACAP#1 应该是完全统一的,但它们都表示通过擦除 getMap() 签名中的实际类型而引入的存在类型。

谁能发现这里出了什么问题? javac 6 是否错误地接受了这段代码?此外,是否有一种不太侵入性(并且兼容 javac 6)的方式来引导 javac 8 走向正确的统一?

编辑:我尝试了从 stackoverflow.com/questions/23063474/ 引入变量的建议,但这似乎没有帮助,我得到了相同的编译错误。

EDIT2:阐明了示例代码的“意图”。

EDIT3:将Map重命名为MyMap,显然定义自定义Map类型太混乱了。

【问题讨论】:

  • 对我来说,错误似乎是首先编写这样的代码。当你读了 5 行代码 5 次,你仍然不知道发生了什么……这总是让我毛骨悚然。
  • 只是为了记录:“地图”是您自己的课程吗?否则 new Map() 不能用 any java 编译器编译。
  • 我发现这段代码的意图很难理解。 Unification 没有泛型,但它的内部类有泛型?那么这背后的想法是什么?
  • 这是抽象掉所有其他细节的结果。我只是展示了这个问题,对代码没有任何语义意义。

标签: java typeerror


【解决方案1】:

可以通过引入中间方法来规避该问题,从而强制 javac 分两个阶段进行类型推断。第一种方法只接受一个类型参数,而将第二个作为通配符。 Javac 能够正确推断此方法的单一类型参数。然后,第二个也是原始方法能够从提供的参数中正确推断出第二个类型参数。

public class Unification {

    final class Box<A> {}

    final class MyMap<A, B extends Box<? extends A>> {}

    MyMap<?, ?> getMap() {
        return new MyMap<Object, Box<Object>>();
    }

    <A> void setMap(final MyMap<A, ?> m) {
        doSetMap(m);
    }

    <A, B extends Box<? extends A>> void doSetMap(final MyMap<A, B> m) {}

    void worksFineNow() {
        setMap(getMap());
    }

}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-11-10
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多