Метод Java 8 Ссылка на нестатический метод

Почему это не работает? Я получаю ошибку компилятора "Не могу сделать статическую ссылку на печать нестатического метода..."

public class Chapter3 {
    public void print(String s) {
        System.out.println(s);
    }
    public static void main(String[] args) {
        Arrays.asList("a", "b", "c").forEach(Chapter3::print);
    }
}

Ответ 1

Независимо от того, используете ли вы ссылки на методы, лямбда-выражения или обычные вызовы методов, для метода экземпляра требуется соответствующий экземпляр для вызова. Экземпляр может быть предоставлен вызовом функции, например. если forEach ожидалось a BiConsumer<Chapter3,String>, это сработало. Но так как forEach ожидает Consumer<String> в вашем случае, экземпляра Chapter3 не существует. Вы можете легко это исправить, изменив метод Chapter3.print на static или предоставив экземпляр в качестве цели для вызова метода:

public class Chapter3 {
    public void print(String s) {
        System.out.println(s);
    }
    public static void main(String[] args) {
        Arrays.asList("a", "b", "c").forEach(new Chapter3()::print);
    }
}

Здесь результат new Chapter3(), новый экземпляр Chapter3, будет захвачен для ссылки метода на его метод print и можно создать метод Consumer<String>, вызывающий метод в этом экземпляре.

Ответ 2

forEach принимает Consumer<? super T> (его подпись default void forEach(Consumer<? super T> action)), который является функциональным интерфейсом с методом accept(T t), который имеет один аргумент.

Когда вы передаете ссылку на нестатический метод метода, который имеет аргумент, у вас фактически есть два аргумента - ссылка this на экземпляр Chapter3 и аргумент String. Это не соответствует ожидаемому forEach.

Ответ 3

Думаю, я понял это сейчас. Что в потоке имеет тип String, поэтому я не могу вызвать печать на String intance...

Например, это работает

public class Chapter3 {
final String value;

public Chapter3(String value) {
    this.value = value;
}

public void print() {
    System.out.println(value);
}

public static void main(String[] args) {
    Arrays.asList(new Chapter3("a"), new Chapter3("b")).forEach(Chapter3::print);
}
}

Ответ 4

На всякий случай, если вы пытаетесь применить метод экземпляра из того же объекта, где выполняется ваш код

Arrays.asList("a", "b", "c").forEach(this::print);

Ответ 5

Если вы не соответствуете типам функции и объектам, из которых вы ее генерируете, вы увидите нестатическую ошибку. Например, эта строка кода не будет компилироваться, потому что функция ожидает Foo в качестве типа, на который она действует, но функция предназначена для Foobar:

Function<Foo, Bar> func = Foobar::getBar;

Он не просто имеет дело с тем, когда он находится в цикле for или любым другим аргументом, и при этом он не должен иметь дело с тем, что находится в области видимости. Это ошибка несоответствия типов, которая неправильно помечена Java при использовании новых объектов функции. Сравните это с тем, что происходит, когда вы создаете другие дженерики:

List<Foo> list = new ArrayList<Bar>();

Эта строка кода не скомпилируется с ошибкой "Несовместимые типы". Еще лучше то, что этот код также не будет работать с несовместимыми типами, несмотря на то, что он также работает с функциональными объектами почти таким же образом:

public void test() {
    Function<Foo, Double> test2 = Foo::getDouble;
    //fails with Incompatible types
    test3(test2);
}


public void test3(Function<Foobar, Double> function) {
    //who cares
}

Почему java выбрал "нестатический метод, на который нельзя ссылаться из статического контекста", мне не понятно.

Ответ 6

Вы можете сделать вашу функцию print static, чтобы вам не требовался экземпляр для ее вызова:

public class Chapter3 {
    public static void print(String s) {
        System.out.println(s);
    }
    public static void main(String[] args) {
        Arrays.asList("a", "b", "c").forEach(Chapter3::print);
    }
}