当涉及lambda时,java实际上是基于泛型的重载方法吗?

当涉及lambda时,java实际上是基于泛型的重载方法吗?,java,generics,lambda,overloading,Java,Generics,Lambda,Overloading,我完全理解泛型的擦除,因此java不能仅基于泛型进行重载。但是如果我们考虑lambda表达式呢 请参见以下案例 interface base{ void test(); } // compiler cant distinguish these two interfaces by lambda expression we provided interface Foo extends base{} interface Boo extends base{} public static vo

我完全理解泛型的擦除,因此java不能仅基于泛型进行重载。但是如果我们考虑lambda表达式呢

请参见以下案例

interface base{
    void test();
}
// compiler cant distinguish these two interfaces by lambda expression we provided 
interface Foo extends base{}
interface Boo extends base{}

public static void main(String[] args) throws ClassNotFoundException {
    HashMap<String,Long> set = new HashMap<>();
    // this is valid, since type Foo is explicitly given out
    t1(set,new Foo(){
        @Override
        public void test(){
        }
    }); // "Long"

    // using lambda expression make the second argument indistinguishable, 
    //so the overloading can only depend on the first argument which is a
    //Hashmap with generic type.
    // but the compiler still managed to work this out
    t1(set,()->{});// "Long"

    HashMap<String,Integer> set2 = new HashMap<>();
    t1(set2,()->{});// "Integer"
}
public static void t1(HashMap<String,Long> set,Foo foo){System.out.println("Long");}
public static void t1(HashMap<String,Integer> set,Boo boo){System.out.println("Integer");}
接口基础{
无效试验();
}
//编译器无法通过我们提供的lambda表达式来区分这两个接口
接口Foo扩展基{}
接口Boo扩展基{}
公共静态void main(字符串[]args)引发ClassNotFoundException{
HashMap set=新的HashMap();
//这是有效的,因为类型Foo是显式给出的
t1(集合,新Foo(){
@凌驾
公开无效测试(){
}
});/“长”
//使用lambda表达式使第二个参数无法区分,
//因此重载只能依赖于第一个参数,即
//具有泛型类型的Hashmap。
//但编译器仍然设法解决了这个问题
t1(集合,()->{});/“长”
HashMap set2=新的HashMap();
t1(set2,()->{});/“整数”
}
公共静态void t1(HashMap集,Foo-Foo){System.out.println(“Long”);}
publicstaticvoidt1(HashMap集,booboo){System.out.println(“Integer”);}
每个函数调用的结果完全符合我的预期,没有任何编译或运行时错误。这就是整件事中令人奇怪的部分:突然之间,函数基于泛型类型重载

那么幕后到底发生了什么呢?

一个有趣的问题

编译器在编译时决定lambda函数的类型,因此即使泛型在该点被删除,函数的类型也在该点被设置,这意味着它知道必须调用什么方法

我的猜测是它可以做到这一点,因为它在那个阶段仍然拥有泛型信息——也就是说,是编译器清除了它

如果您查看类的字节码,它会说:

16: invokestatic  #25                 // Method t1:
     (Ljava/util/HashMap;LTest$Foo;)V
25: invokestatic  #25  // Method t1:   
    (Ljava/util/HashMap;LTest$Foo;)V

因此,对于HashMap,类型已经消失,但是对于接口,类型已经设置。

我不理解您的问题。您正在传递一个
HashMap
。为什么会与其他方法混淆?这基本上是重复的。请参阅接受的答案。@SotiriosDelimanolis,因为java不会在泛型类型上重载。HashMap和HashMap在编译后是一样的。@判断不判断在发布这个问题之前,我已经检查了这个问题。不同之处在于,在我的例子中,编译器不知道要使用哪个接口,所以它不得不检查第一个参数,这是一个泛型类型的hashmap。这不是关于lambda,而是关于泛型类型的重载。在您的注释中,您谈论的是单个参数。你的问题不是这样的。编译器必须明确地选择一个适用的方法。当您使用两个参数调用
t
,其中第一个参数是
HashMap
时,唯一适用的方法是第二个参数是
Foo
,但不是因为它是
Foo
,但是因为第一个参数是一个
HashMap
。所以我们确实可以使用这个技巧来强制编译器基于泛型类型进行重载?我想是的,但是如果您必须有一个不同的接口来表示每个可能的变体,那么它在某种程度上否定了您可能从中获得的任何优势。