简体   繁体   English

Eclipse Java编译器和OpenJDK编译器(Java 8)之间的类型推断差异

[英]Type inference discrepancy between Eclipse Java compiler and OpenJDK compiler (Java 8)

I'm doing some meta programming where I'm parsing JAXB beans, particularly XmlAdapter annotations. 我正在做一些元编程,在其中解析JAXB bean,尤其是XmlAdapter注释。 I arrived at a situation where the compiler of Eclipse Oxygen (4.7.2) is fully content with some methods that use type inference, but the OpenJDK compiler (javac 1.8.0_131) chokes on them. 我遇到了一种情况,其中Eclipse Oxygen(4.7.2)的编译器完全满足于使用类型推断的某些方法,但是OpenJDK编译器(javac 1.8.0_131)使它们窒息。

I've extracted the principal structure into this MWE: 我已将主体结构提取到此MWE中:

import java.time.LocalDate;

import javax.xml.bind.annotation.adapters.XmlAdapter;

public class Main<BoundType>
{
   public void parse( Class<? extends XmlAdapter<?, BoundType>> adapterClass ) throws Exception
   {
      process( adapterClass );
   }

   private <ValueType> void process( Class<? extends XmlAdapter<ValueType, BoundType>> adapterClass ) throws Exception
   {
      // Do something with adapterClass ...
   }

   private static final class SomeAdapter extends XmlAdapter<String, LocalDate>
   {
      @Override
      public LocalDate unmarshal( String v ) throws Exception
      {
         return v == null ? null : LocalDate.parse( v );
      }

      @Override
      public String marshal( LocalDate v ) throws Exception
      {
         return v == null ? null : v.toString();
      }
   }

   public static void main( String[] args ) throws Exception
   {
      Main<LocalDate> main = new Main<>();

      main.parse( SomeAdapter.class );
   }
}

The OpenJDK compiler produces this error: OpenJDK编译器会产生此错误:

/[...]/WildcardProblem/src/Main.java:21: error: method process in class Main<BoundType> cannot be applied to given types;
      process( adapterClass );
      ^
  required: Class<? extends XmlAdapter<ValueType,BoundType>>
  found: Class<CAP#1>
  reason: cannot infer type-variable(s) ValueType
    (argument mismatch; Class<CAP#1> cannot be converted to Class<? extends XmlAdapter<ValueType,BoundType>>)
  where ValueType,BoundType are type-variables:
    ValueType extends Object declared in method <ValueType>process(Class<? extends XmlAdapter<ValueType,BoundType>>)
    BoundType extends Object declared in class Main
  where CAP#1 is a fresh type-variable:
    CAP#1 extends XmlAdapter<?,BoundType> from capture of ? extends XmlAdapter<?,BoundType>

I can work around the compiler error by modifying method process like this: 我可以通过修改方法process来解决编译器错误:

   private <ValueType> void process( Class<? extends XmlAdapter<?, BoundType>> adapterClass ) throws Exception
   {
      @SuppressWarnings( "unchecked" )
      Class<? extends XmlAdapter<ValueType, BoundType>> capturedAdapterClass = (Class<? extends XmlAdapter<ValueType, BoundType>>) adapterClass;

      // Do something with adapterClass ...
   }

But of course, the unchecked cast is ugly. 但是,当然,未经检查的演员阵容很难看。 I believe that it should be safe though because the type parameter ValueType is introduced arbitrarily here only to capture the wildcard. 认为这应该是安全的,因为在这里任意引入类型参数ValueType只是为了捕获通配符。 I just need it further down in my processing where I instantiate an XmlAdapter and call unmarshal on it. 在处理过程中,我只需要进一步实例化一个XmlAdapter并对其进行unmarshal At runtime, no exceptions occur. 在运行时,不会发生任何异常。

So my questions are, first of all, is the original code supposed to compile, and second, would this be an Eclipse compiler or an OpenJDK compiler bug? 因此,我的问题是,首先是应该编译原始代码,其次,这是Eclipse编译器还是OpenJDK编译器错误?

Not a bug. 不是错误。 You are playing with nested generics here. 您在这里使用嵌套泛型。

Class<? extends XmlAdapter<?, BoundType>> Class<? extends XmlAdapter<?, BoundType>> is not the same as Class<? extends XmlAdapter<ValueType, BoundType>> Class<? extends XmlAdapter<?, BoundType>>Class<? extends XmlAdapter<ValueType, BoundType>> Class<? extends XmlAdapter<ValueType, BoundType>> . Class<? extends XmlAdapter<ValueType, BoundType>>

You must change the method signature to: 您必须将方法签名更改为:

public <ValueType> void parse( Class<? extends XmlAdapter<ValueType, BoundType>> adapterClass )

or cast as you did before. 或像以前一样进行投射


Simpler example: 比较简单的例子:

public void parse2(List<? extends Optional<?>> optionals) {
    process2(optionals); // compilation error
}

public <T> void process2(List<? extends Optional<T>> optionals) {

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM