方法参考中的类型推断
我最近把手放在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);