方法参考中的类型推断

我最近把手放在Java 8上并尝试使用方法参考。 我正在尝试不同类型的方法引用,并陷入“引用特定类型的任意对象的实例方法”类型中。

String[] arr = {"First", "Second", "Third", "Fourth"}; Arrays.sort(arr, String::compareToIgnoreCase); 

这非常有效。 但是当我尝试通过其类型引用用户定义类的方法时:

 Demo2[] arr = {a, b}; Arrays.sort(arr, Demo2::compare); 

这将编译时错误显示为“无法从静态上下文引用非静态方法”。

这是Demo2类:

 public class Demo2 implements Comparator { Integer i; Demo2(Integer i1){ i = i1; } public Integer getI() { return i; } @Override public int compare(Demo2 o1, Demo2 o2) { return o1.getI().compareTo(o2.getI()); } } 

正如greg-449指出的那样,你的代码有一个bug。

通过像YourObjet::yourMethod这样的方法引用,您可以对实例的方法进行静态引用。 因此,将为每个对象调用该方法,因此签名需要与之前的不同

将编译的代码将具有以下forms:

 Demo2[] demos = {a, b}; Arrays.sort(demos, Demo2::compareTo); public class Demo2 { Integer i; Demo2(Integer i1){ i = i1; } public Integer getI() { return i; } public int compareTo(Demo2 other) { return this.getI().compareTo(other.getI()); } } 

但正如RealSkeptic指出的那样,这不是实施和对象比较的正确方法。 你应该给Arrays.sort方法一个比较器:

  Arrays.sort(demos, (obj1, obj2) -> obj1.getI().compareTo(obj2.getI())); 

Arrays.sort(T[],Comparator)所需的Comparator接口有一个方法,它接受两个相同类型T对象引用,并返回一个整数。

方法引用中有一些“魔力”。 Java所做的是以适合接口要求的方式包装方法。

当然,接口不需要静态方法。 但是包装可以创建一个调用静态方法的方法,如教程的第一个例子:

 public static int compareByAge(Person a, Person b) { return a.birthday.compareTo(b.birthday); } 

它包装它的方式使你得到类似的东西

 new Comparator() { @Override public int compare(Person a, Person b) { return Person.compareByAge(a,b); } } 

这满足了界面。

但是在“引用特定类型的任意对象的实例方法”一节中的示例中,它以不同的方式包装它。 它需要一个接收两个字符串的方法,但它有一个只接收一个字符串的方法。 这是String::compareToIgnoreCase的定义方式:

 public int compareToIgnoreCase(String str) 

但在这种情况下,它是一个实例方法。 Java现在做的是,因为这个方法属于String类型的对象,并且接受String类型的对象,所以很容易在它周围构建一个“wrap”,使它成为一个接受两个对象的方法,就像lambda一样表达:

 (String a, String b) -> { return a.compareToIgnoreCase( b ); } 

或者,如果我们想象一个正式的包装作为Comparator

 new Comparator() { @Override public int compare(String a, String b) { return a.compareToIgnoreCase(b); } } 

因此,它是属于类型T的实例方法,接受类型T并返回int这一事实允许Java适当地包装它以使其适合Comparator接口。

但是int compare(Demo2 o1, Demo2 o2)不适合这种模式。 它接受两个参数。 如果一个方法接受两个参数,它必须是一个静态方法来适应包装规则 – 没有办法将“this”对象传递给Comparator接口。 因此它尝试将其包装为静态方法,并且失败,因为它不是静态方法。

底线:你得到了错误,因为对于这种特殊类型的方法引用,你需要一个只有一个与类相同类型的参数的实例方法。

正如@Holger在评论中提到的那样,如果您正在构建一个新类,则不应该在其中专门为此类排序任务添加比较方法。 如果类具有自然顺序,请将其Comparable并使用Arrays.sort(Object[]) 。 如果没有,并且您需要根据其任何属性对其进行排序,请使用lambda表达式或Comparator.comparing(Demo2::getI) ,它可以更好地利用现有的getter来进行比较。

作为惯例, Comparator使用lambda表达式实现,在类中实现它看起来很奇怪。

  Demo2[] array = new Demo2[2]; array[0] = new Demo2(12); array[1] = new Demo2(32); Comparator demo2Comparator = (e1,e2)->e1.getI().compareTo(e2.getI()); Arrays.sort(array, demo2Comparator);