方法参考中的类型推断

时间:2016-08-22 07:40:12

标签: java java-8 method-reference

我最近把手放在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<Demo2> {
    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());
    }
}

3 个答案:

答案 0 :(得分:5)

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

通过制作类似YourObjet::yourMethod的方法引用,您可以对实例的方法进行静态引用。因此,将为每个对象调用该方法,因此签名需要与早期的

不同

将编译的代码将具有以下形式:

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()));

答案 1 :(得分:3)

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

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

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

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

它以一种类似于

的方式包装它
new Comparator<Person>() {
    @Override
    public int compare(Person a, Person b) {
        return Person.compareByAge(a,b);
    }
}

满足界面。

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

public int compareToIgnoreCase(String str)

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

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

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

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

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

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

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

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

答案 2 :(得分:1)

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

    Demo2[] array = new Demo2[2];
    array[0] = new Demo2(12);
    array[1] = new Demo2(32);

    Comparator<Demo2> demo2Comparator = (e1,e2)->e1.getI().compareTo(e2.getI());
    Arrays.sort(array, demo2Comparator);