Почему этот фрагмент кода, связанный с лямбдой, работает в Java 8? [Дубликат]

Короткий ответ: вам нужно выполнить обратный вызов следующим образом:

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});
760
задан Olimpiu POP 17 December 2014 в 10:50
поделиться

14 ответов

Обычно можно вызвать метод reduce, используя Math.max(int, int) следующим образом:

reduce(new IntBinaryOperator() {
    int applyAsInt(int left, int right) {
        return Math.max(left, right);
    }
});

Для этого требуется много синтаксиса только для вызова Math.max. Вот тут и появляются лямбда-выражения. Начиная с Java 8 разрешено делать то же самое гораздо короче:

reduce((int left, int right) -> Math.max(left, right));

Как это работает? Компилятор java «обнаруживает», что вы хотите реализовать метод, который принимает два int s и возвращает один int. Это эквивалентно формальным параметрам одного и только метода интерфейса IntBinaryOperator (параметр метода reduce, который вы хотите вызвать). Таким образом, компилятор делает для вас все остальное - он просто предполагает, что вы хотите реализовать IntBinaryOperator.

Но поскольку сам Math.max(int, int) выполняет формальные требования IntBinaryOperator, его можно использовать напрямую. Поскольку Java 7 не имеет синтаксиса, который позволяет передавать сам метод как аргумент (вы можете передавать только результаты метода, но никогда не ссылаетесь на методы), синтаксис :: был введен в Java 8 для ссылок на методы:

reduce(Math::max);

Обратите внимание, что это будет интерпретироваться компилятором, а не JVM во время выполнения! Хотя он создает разные байт-коды для всех трех фрагментов кода, они семантически равны, поэтому последние два можно считать короткими (и, вероятно, более эффективными) версиями реализации IntBinaryOperator выше!

(См. также Перевод лямбда-выражений )

839
ответ дан Jared Burrows 26 August 2018 в 08:01
поделиться

Во время выполнения они ведут себя точно так же. Байт-код может / не быть таким же (для выше Incase он генерирует один и тот же байт-код (см. выше и проверьте javaap -c;))

Во время выполнения они вести себя точно так же.method (math :: max) ;, он генерирует одну и ту же математику (см. выше и проверить javap -c;))

2
ответ дан Alfa khatoon 26 August 2018 в 08:01
поделиться

:: - новый оператор, включенный в Java 8, который используется для ссылки на метод существующего класса. Вы можете ссылаться на статические методы и нестатические методы класса.

Для ссылки на статические методы синтаксис:

ClassName :: methodName 

Для ссылки на нестатические методы синтаксис

objRef :: methodName

И

ClassName :: methodName

Единственное обязательное условие для обращения к методу заключается в том, что метод существует в функциональном интерфейсе, который должен быть совместим с ссылкой метода.

Ссылки на методы, при оценке, создают экземпляр функционального интерфейса.

Найдено по: http://www.speakingcs.com/2014/08/method-references-in-java-8.html

22
ответ дан Esteban Herrera 26 August 2018 в 08:01
поделиться

Я нашел этот источник очень интересным.

На самом деле именно Лямбда превращается в Двойной Колон. Двойной Колон более читабельен. Мы выполняем следующие шаги:

STEP1:

// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

STEP2:

// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

STEP3:

// The magic
Comparator c = Comparator.comparing(Person::getAge());
1
ответ дан HoratioCain 26 August 2018 в 08:01
поделиться

Поскольку многие ответы здесь хорошо объясняли поведение ::, я хотел бы уточнить, что оператор :: не должен иметь точно такую ​​же подпись, как ссылающийся функциональный интерфейс, если он используется для переменных экземпляра. Допустим, нам нужен BinaryOperator , который имеет тип TestObject. Традиционным способом это реализовано следующим образом:

BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {

        @Override
        public TestObject apply(TestObject t, TestObject u) {

            return t;
        }
    };

Как вы видите в анонимной реализации, он требует два аргумента TestObject и возвращает объект TestObject. Чтобы удовлетворить это условие с помощью оператора ::, мы можем начать со статического метода:

public class TestObject {


    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

, а затем вызвать:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Хорошо, он скомпилирован. А если нам нужен метод экземпляра? Давайте обновим TestObject с помощью метода экземпляра:

public class TestObject {

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Теперь мы можем получить доступ к экземпляру, как показано ниже:

TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;

Этот код компилируется отлично, но ниже одного нет:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Мое затмение говорит мне: «Невозможно сделать статическую ссылку на нестатический метод testInstance (TestObject, TestObject) из типа TestObject ...»

Достаточно справедливо его метод экземпляра, но если мы перегружаем testInstance, как показано ниже:

public class TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

И вызов:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Код будет точно компилироваться. Потому что он будет вызывать testInstance с единственным параметром вместо двойного. Хорошо, так что случилось с нашими двумя параметрами? Позволяет распечатать и увидеть:

public class TestObject {

    public TestObject() {
        System.out.println(this.hashCode());
    }

    public final TestObject testInstance(TestObject t){
        System.out.println("Test instance called. this.hashCode:" 
    + this.hashCode());
        System.out.println("Given parameter hashCode:" + t.hashCode());
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Который выведет:

 1418481495  
 303563356  
 Test instance called. this.hashCode:1418481495
 Given parameter hashCode:303563356

Хорошо, поэтому JVM достаточно умен, чтобы вызвать param1.testInstance (param2). Можем ли мы использовать testInstance из другого ресурса, но не TestObject, то есть:

public class TestUtil {

    public final TestObject testInstance(TestObject t){
        return t;
    }
}

И вызов:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

Он просто не компилируется, и компилятор скажет: « type TestUtil не определяет testInstance (TestObject, TestObject) ". Поэтому компилятор будет искать статическую ссылку, если это не тот же тип. Хорошо, что о полиморфизме? Если мы удалим финальные модификаторы и добавим наш класс SubTestObject:

public class SubTestObject extends TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

}

И вызов:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

Он также не будет скомпилирован, компилятор по-прежнему будет искать статическую ссылку. Но ниже код будет компилироваться отлично, поскольку он проходит is-test:

public class TestObject {

    public SubTestObject testInstance(Object t){
        return (SubTestObject) t;
    }

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* Я просто изучаю, поэтому я понял, что попробовал и посмотрю, не стесняйтесь исправлять меня, если я неправильно

1
ответ дан HRgiger 26 August 2018 в 08:01
поделиться

return reduce(Math::max); НЕ РАВНО return reduce(max());

Но это означает, что-то вроде этого:

IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);

Вы можете просто сохранить 47 нажатий клавиш если вы пишете так

return reduce(Math::max);//Only 9 keystrokes ^_^
2
ответ дан Jude Niroshan 26 August 2018 в 08:01
поделиться

В более старых версиях Java вместо «::» или lambd вы можете использовать:

public interface Action {
    void execute();
}

public class ActionImpl implements Action {

    @Override
    public void execute() {
        System.out.println("execute with ActionImpl");
    }

}

public static void main(String[] args) {
    Action action = new Action() {
        @Override
        public void execute() {
            System.out.println("execute with anonymous class");
        }
    };
    action.execute();

    //or

    Action actionImpl = new ActionImpl();
    actionImpl.execute();
}

Или перейти к методу:

public static void doSomething(Action action) {
    action.execute();
}
1
ответ дан Kamil Tomasz Jarmusik 26 August 2018 в 08:01
поделиться

Кажется, его немного поздно, но вот мои два цента. Для создания анонимных методов используется выражение lambda . Он ничего не делает, кроме вызова существующего метода, но яснее ссылаюсь на метод непосредственно по его имени. И ссылка метода позволяет нам сделать это с помощью оператора ссылки на метод ::.

Рассмотрим следующий простой класс, в котором каждый сотрудник имеет имя и оценку.

public class Employee {
    private String name;
    private String grade;

    public Employee(String name, String grade) {
        this.name = name;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

Предположим, у нас есть список сотрудников, возвращаемых каким-то методом, и мы хотим сортировать сотрудников по их классу. Мы знаем, что мы можем использовать анонимный класс как:

    List<Employee> employeeList = getDummyEmployees();

    // Using anonymous class
    employeeList.sort(new Comparator<Employee>() {
           @Override
           public int compare(Employee e1, Employee e2) {
               return e1.getGrade().compareTo(e2.getGrade());
           }
    });

, где getDummyEmployee () - это некоторый метод как:

private static List<Employee> getDummyEmployees() {
        return Arrays.asList(new Employee("Carrie", "C"),
                new Employee("Farhan", "F"),
                new Employee("Brian", "B"),
                new Employee("Donald", "D"),
                new Employee("Adam", "A"),
                new Employee("Evan", "E")
                );
    }

Теперь мы знаем что Comparator является функциональным интерфейсом. Функциональный интерфейс - это тот, который имеет только один абстрактный метод (хотя он может содержать один или несколько стандартных или статических методов). Поэтому мы можем использовать лямбда-выражение как:

employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // lambda exp

Кажется, все хорошо, но что, если класс Employee также предоставляет аналогичный метод:

public class Employee {
    private String name;
    private String grade;
    // getter and setter
    public static int compareByGrade(Employee e1, Employee e2) {
        return e1.grade.compareTo(e2.grade);
    }
}

В этом случае, используя имя метода будет более понятным. Следовательно, мы можем напрямую ссылаться на метод, используя ссылку на метод:

employeeList.sort(Employee::compareByGrade); // method reference

В соответствии с docs существует четыре типа ссылок на методы:

+----+-------------------------------------------------------+--------------------------------------+
|    | Kind                                                  | Example                              |
+----+-------------------------------------------------------+--------------------------------------+
| 1  | Reference to a static method                          | ContainingClass::staticMethodName    |
+----+-------------------------------------------------------+--------------------------------------+
| 2  |Reference to an instance method of a particular object | containingObject::instanceMethodName | 
+----+-------------------------------------------------------+--------------------------------------+
| 3  | Reference to an instance method of an arbitrary object| ContainingType::methodName           |
|    | of a particular type                                  |                                      |  
+----+-------------------------------------------------------+--------------------------------------+
| 4  |Reference to a constructor                             | ClassName::new                       |
+------------------------------------------------------------+--------------------------------------+
11
ответ дан Konrad Borowski 26 August 2018 в 08:01
поделиться

Да, это правда. Оператор :: используется для ссылки на метод. Таким образом, можно извлекать статические методы из классов, используя его или методы из объектов. Тот же оператор может использоваться даже для конструкторов. Все примеры, упомянутые здесь, приведены в примере кода ниже.

Официальную документацию из Oracle можно найти здесь здесь .

Вы можете получить лучший обзор JDK 8 изменяет в эту статью . В разделе ссылки «Метод / конструктор» приведен пример кода:

interface ConstructorReference {
    T constructor();
}

interface  MethodReference {
   void anotherMethod(String input);
}

public class ConstructorClass {
    String value;

   public ConstructorClass() {
       value = "default";
   }

   public static void method(String input) {
      System.out.println(input);
   }

   public void nextMethod(String input) {
       // operations
   }

   public static void main(String... args) {
       // constructor reference
       ConstructorReference reference = ConstructorClass::new;
       ConstructorClass cc = reference.constructor();

       // static method reference
       MethodReference mr = cc::method;

       // object method reference
       MethodReference mr2 = cc::nextMethod;

       System.out.println(cc.value);
   }
}
49
ответ дан Mauli Shah 26 August 2018 в 08:01
поделиться

В java-8 Streams Reducer в простых работах - это функция, которая принимает два значения в качестве входных данных и возвращает результат после некоторых вычислений. этот результат подается на следующей итерации.

в случае функции Math: max, метод сохраняет возвращаемое значение max двух значений и, в конце концов, у вас наибольшее количество в руке.

2
ответ дан Pramod 26 August 2018 в 08:01
поделиться

Это ссылка на метод в Java 8. Документация оракула здесь .

Как указано в документации ...

Ссылка метода Person :: compareByAge является ссылкой на статический метод.

Ниже приведен пример ссылки на метод экземпляра конкретного объекта:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }

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

ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 

Ссылка на метод myComparisonProvider :: compareByName вызывает метод compareByName, который является частью объекта myComparisonProvider. JRE запрашивает аргументы типа метода, которые в этом случае являются (Person, Person).

18
ответ дан Program-Me-Rev 26 August 2018 в 08:01
поделиться

:: называется ссылкой на метод. Это в основном ссылка на один метод. То есть он ссылается на существующий метод по имени.

Краткое пояснение: Ниже приведен пример ссылки на статический метод:

class Hey {
     public static double square(double num){
        return Math.pow(num, 2);
    }
}

Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);

square можно передавать так же, как ссылки на объекты и запускаются при необходимости. Фактически, его можно так же легко использовать в качестве ссылки на «нормальные» методы объектов как static. Например:

class Hey {
    public double square(double num) {
        return Math.pow(num, 2);
    }
}

Hey hey = new Hey();
Function<Double, Double> square = hey::square;
double ans = square.apply(23d);

Function выше - функциональный интерфейс. Чтобы полностью понять ::, важно также понимать функциональные интерфейсы. По-видимому, функциональный интерфейс является интерфейсом только с одним абстрактным методом.

Примеры функциональных интерфейсов включают Runnable, Callable и ActionListener.

Function выше - функциональный интерфейс только с одним способом: apply. Он принимает один аргумент и дает результат.


Причина, по которой :: s является awesome, - , что :

Ссылки на методы являются выражениями, которые имеют такое же отношение к лямбда-выражениям (...), но вместо предоставления тела метода они ссылаются на существующий метод по имени.

Например вместо записи лямбда-тела

Function<Double, Double> square = (Double x) -> x * x;

Вы можете просто выполнить

Function<Double, Double> square = Hey::square;

Во время выполнения эти два метода square ведут себя точно так же, как и другие. Байт-код может быть или не быть одинаковым (хотя для вышеуказанного случая генерируется один и тот же байт-код, компилируется выше и проверяется с помощью javap -c).

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

Ниже указано незаконное:

Supplier<Boolean> p = Hey::square; // illegal

square ожидает аргумент и возвращает значение double. Метод get в Поставщик ожидает аргумент, но ничего не возвращает. Таким образом, это приводит к ошибке.

Ссылка на метод относится к методу функционального интерфейса. (Как уже упоминалось, функциональные интерфейсы могут иметь только один метод).

Еще несколько примеров: метод accept в Пользователь принимает ввод, но ничего не возвращает.

Consumer<Integer> b1 = System::exit;   // void exit(int status)
Consumer<String[]> b2 = Arrays::sort;  // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)

class Hey {
    public double getRandom() {
        return Math.random();
    }
}

Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result

Выше getRandom не принимает аргументов и возвращает double. Таким образом, любой функциональный интерфейс, который удовлетворяет критериям: , не принимает аргументов и возвращает double .

Другой пример:

Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");

В случае параметризованных типов:

class Param<T> {
    T elem;
    public T get() {
        return elem;
    }

    public void set(T elem) {
        this.elem = elem;
    }

    public static <E> E returnSame(E elem) {
        return elem;
    }
}

Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;

Function<String, String> func = Param::<String>returnSame;

Ссылки на методы могут иметь разные стили, но в корне они все означают одинаковые вещь и может быть просто визуализирована как lambdas:

  1. Статический метод (ClassName::methName)
  2. Метод экземпляра конкретного объекта (instanceRef::methName)
  3. Супер метод конкретного объекта (super::methName)
  4. Метод экземпляра произвольного объекта определенного типа (ClassName::methName)
  5. Ссылка на конструктор класса ( ClassName::new)
  6. Ссылка на конструктор массива (TypeName[]::new)

Дальнейшую ссылку см. в http://cr.openjdk.java.net /~briangoetz/lambda/lambda-state-final.html.

387
ответ дан SOLO 26 August 2018 в 08:01
поделиться

:: известен как ссылки на методы. Допустим, мы хотим вызвать метод calculatePrice класса Purchase. Затем мы можем записать его как:

Purchase::calculatePrice

Его также можно рассматривать как короткую форму записи выражения лямбда. Так как ссылки методов преобразуются в лямбда-выражения.

3
ответ дан Sonu 26 August 2018 в 08:01
поделиться

:: Оператор был введен в java 8 для ссылок на методы. Ссылка на метод - это сокращенный синтаксис выражения лямбда, который выполняет только один метод. Вот общий синтаксис ссылки на метод:

Object :: methodName

Мы знаем, что вместо использования анонимного класса мы можем использовать лямбда-выражения . Но иногда выражение лямбда - это просто вызов некоторого метода, например:

Consumer<String> c = s -> System.out.println(s);

Чтобы сделать код более понятным, вы можете превратить это лямбда-выражение в ссылку метода:

Consumer<String> c = System.out::println;
4
ответ дан Vaibhav9518 26 August 2018 в 08:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: