Java类型推断:引用在Java8中是不明确的,但在Java7中不是

Java类型推断:引用在Java8中是不明确的,但在Java7中不是,java,generics,compiler-errors,java-8,type-inference,Java,Generics,Compiler Errors,Java 8,Type Inference,假设我们有2节课。一个空类Base,以及该类派生的子类 public class Base {} public class Derived extends Base {} 然后我们在另一个类中有一些方法: import java.util.Collection public class Consumer { public void test() { set(new Derived(), new Consumer().get()); } publi

假设我们有2节课。一个空类
Base
,以及该类
派生的子类

public class Base {}

public class Derived extends Base {}
然后我们在另一个类中有一些方法:

import java.util.Collection

public class Consumer {

    public void test() {
        set(new Derived(), new Consumer().get());
    }

    public <T extends Base> T get() {
        return (T) new Derived();
    }

    public void set(Base i, Derived b) {
        System.out.println("base");
    }

    public void set(Derived d, Collection<? extends Consumer> o) {
        System.out.println("object");
    }

}

为什么可以在Java 7中工作,而不能在Java 8中工作?
如何匹配集合?

问题在于类型推断已得到改进。你有这样的方法吗

public <T extends Base> T get() {
    return (T) new Derived();
}

回想一下,您的方法
Consumer.get()
说“调用者可以决定我返回什么”。因此,假设可能存在一个扩展
Base
并同时实现
Collection
的类型是完全正确的。因此,编译器说:“我不知道是调用
set(Base I,Derived b)
还是
set(Derived d,Collection好吧,Java7似乎并不乐意运行它。它在给出错误之前给出了几个警告:

jatin@jatin-~$ javac -Xlint:unchecked -source 1.7 com/company/Main.java 
warning: [options] bootstrap class path not set in conjunction with -source 1.7
com/company/Main.java:19: error: no suitable method found for set(Derived,Base)
        set(new Derived(), new Consumer().get());
        ^
    method Consumer.set(Base,Derived) is not applicable
      (argument mismatch; Base cannot be converted to Derived)
    method Consumer.set(Derived,Collection<? extends Consumer>) is not applicable
      (argument mismatch; Base cannot be converted to Collection<? extends Consumer>)

com/company/Main.java:28: warning: [unchecked] unchecked cast
        return (T) new Derived();
                   ^
  required: T
  found:    Derived
  where T is a type-variable:
    T extends Base declared in method <T>get()
当我们做
newconsumer().get()时
,如果我们看一下
get
的签名,它会返回一个类型
T
。我们知道
T
以某种方式扩展了
Base
。但是我们不知道
T
具体是什么。它可以是任何东西。所以,如果我们不能明确地确定
T
是什么,那么编译器怎么能呢

告诉编译器的一种方法是硬编码,并通过以下方式明确地告诉它:
set(new-Derived(),new-Consumer().get());

上述原因极其(故意重复)危险,是因为当您尝试这样做时:

class NewDerived extends Base {
     public String getName(){return "name";};
}
NewDerived d = new Consumer().<NewDerived>get();
System.out.println(d.getName());
同样,它找不到将
派生、基
作为参数的正确方法。因此它抛出错误,但原因不同:

set(新派生(),新使用者().get());
^
方法Consumer.set(基,派生)不适用
(参数不匹配;无法将基转换为派生)

方法Consumer.set(派生,CollectionWell,它用Java 8Eclipse编译对我来说很好,但是
javac
不能。假设您使用
javac
,安全吗?@Jeffrey是的。我用javac和IntelliJ从命令行编译时遇到这个错误。我使用的是Java 1.8.0版,您可以让一些东西扩展基本实现Collective在…上,这是同一个问题,除了Java 8中的东西已经“改进”(事实上,规范是不直观的,但编译器根据规范正确运行)实际上,Java 8编译器没有那么聪明。事实上,
new Consumer().get()
可以毫无问题地编译。提问者的编译器错误并没有说明未检查的操作有多错误,只是说方法调用不明确。这意味着删除两个
set
方法中的一个(无论哪一个!)将“修复”编译器错误,即使结果是错误的方法(应为
集合
)将被调用。或者,好吧,尝试,因为从
派生的
集合的转换将失败。顺便说一句。Java 7编译器不会给出警告,它会给出错误。只有输出的第一行是警告。@Holger我完全同意。谢谢你指出。我脑子里还有别的想法,我试着指出了一个不同之处非常感谢。编辑了答案。再次感谢!
public <X extends Base&Collection<Consumer>> void test() {
    set(new Derived(), new Consumer().<X>get());
}
jatin@jatin-~$ javac -Xlint:unchecked -source 1.7 com/company/Main.java 
warning: [options] bootstrap class path not set in conjunction with -source 1.7
com/company/Main.java:19: error: no suitable method found for set(Derived,Base)
        set(new Derived(), new Consumer().get());
        ^
    method Consumer.set(Base,Derived) is not applicable
      (argument mismatch; Base cannot be converted to Derived)
    method Consumer.set(Derived,Collection<? extends Consumer>) is not applicable
      (argument mismatch; Base cannot be converted to Collection<? extends Consumer>)

com/company/Main.java:28: warning: [unchecked] unchecked cast
        return (T) new Derived();
                   ^
  required: T
  found:    Derived
  where T is a type-variable:
    T extends Base declared in method <T>get()
set(new Derived(), new Consumer().get());
class NewDerived extends Base {
     public String getName(){return "name";};
}
NewDerived d = new Consumer().<NewDerived>get();
System.out.println(d.getName());
Base base = new Consumer().get();
set(new Derived(), base);
    set(new Derived(), new Consumer().get());
    ^
method Consumer.set(Base,Derived) is not applicable
  (argument mismatch; Base cannot be converted to Derived)
method Consumer.set(Derived,Collection<? extends Consumer>) is not applicabl e
  (argument mismatch; Base cannot be converted to Collection<? extends Consu mer>)