Squeak.ru - шаблоны программирования

:: (двойное двоеточие) оператор в Java 8

Я изучал исходный код Java 8 и обнаружил, что эта конкретная часть кода очень удивительна:

//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
    return evaluate(ReduceOps.makeInt(op));
}

@Override
public final OptionalInt max() {
    return reduce(Math::max); //this is the gotcha line
}

//defined in Math.java
public static int max(int a, int b) {
    return (a >= b) ? a : b;
}

Math::max что-то вроде указателя на метод? Как обычный static метод преобразуется в IntBinaryOperator?

15.11.2013

  • Синтаксический сахар заключается в том, что компилятор автоматически генерирует реализации интерфейса на основе предоставляемой вами функции (чтобы упростить использование всей лямбда-функции с существующими базами кода). 15.11.2013
  • java.dzone.com/articles/java-lambda-expressions-vs может помочь, не заглядывал глубоко в тему 15.11.2013
  • @ Нет, это не совсем синтаксический сахар, если вы не можете сказать для чего. т.е. x - синтаксический сахар для y. 15.11.2013
  • @Ingo Я думал, что сказал зачем. Вы также можете назвать это аббревиатурой, которая позволяет избежать накладных расходов (кода) на перенос метода самостоятельно в отдельный класс, который реализует IntBinaryOperator и вызывает Math.max. И это поведение я называю «синтаксическим сахаром». 15.11.2013
  • Конечно, @Neet, но ведет ли он себя так же? т.е. появляется ли какой-то файл класса, по-видимому, из ниоткуда, который содержит этот ссылочный класс Math :: max? 15.11.2013
  • @Ingo, поскольку в Java8 нет новых кодов операций, это единственный способ, которым это может работать. Это простой старый анонимный внутренний класс. 15.11.2013
  • @Neet - я боюсь, что вы правы, хотя я думаю, что где-то читал, что они будут создавать классы на лету во время выполнения. Я все еще надеюсь, что они найдут способ избежать всех этих избыточных файлов классов. 15.11.2013
  • @Ingo хм, я сомневаюсь, что они будут создавать классы во время выполнения (хотя ASM теперь является частью JDK). Это приведет к снижению производительности и / или другим вещам, вызывающим ленивую инициализацию. Однако они могли изменить формат файла классов, чтобы внутренние классы содержались в одном файле. Это было бы здорово ^^ 15.11.2013
  • @Ingo он создает новый объект лямбда каждый раз, когда я его использую. TestingLambda$$Lambda$2/8460669 и TestingLambda$$Lambda$3/11043253 были созданы при двух вызовах. 15.11.2013
  • @ Нет, посмотри мой комментарий к тому, что я нашел. При каждом вызове создается новый экземпляр. 15.11.2013
  • @NarendraPathai, не могли бы вы взглянуть на сгенерированный байт-код? Я не знаком с этой схемой именования, и создание нового object - это нормально. 15.11.2013
  • Я просто не могу поверить, что они действительно будут генерировать классы на лету. Это означало бы удвоение загрузки GC (класса и экземпляра) при каждом вызове ... и значительных накладных расходов на создание класса. 15.11.2013
  • @NarendraPathai OMG! Надеюсь, они исправят это до официального выпуска. 15.11.2013
  • @Neet - я думаю, что это можно сделать, когда загружен ссылочный класс - на самом деле это должен быть крошечный класс размером, может быть, 1k (если вообще), который может быть построен очень механическим способом - может быть быстрее, чем на самом деле чтение этого материал из файловой системы или JAR. 15.11.2013
  • @Ingo Да, они должны, это огромная потеря. ИМХО, создание нового объекта при каждом вызове - это слишком много. 15.11.2013
  • Лямбды и ссылки на методы - это не простые старые анонимные внутренние классы. См. programmers.stackexchange.com/a/181743/59134. Да, при необходимости, новые классы и экземпляры создаются на лету, при необходимости, но только при необходимости. 16.11.2013
  • Создание класса не является секретом: download.java. сеть / jdk8 / документы / api / java / lang / invoke / 18.11.2013
  • :: используется для передачи «ссылки на метод» конкретному методу. Эта концепция основана на шаблоне «параметризации поведения», в котором параметризуется само «поведение». 11.07.2016
  • Официальное руководство объясняет это: docs.oracle.com/javase/tutorial/ java / javaOO / 26.11.2020

Ответы:


1

Обычно метод 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 и возвращает один int. Это эквивалентно формальным параметрам единственного метода интерфейса IntBinaryOperator (параметр метода reduce, который вы хотите вызвать). Таким образом, компилятор сделает все остальное за вас - он просто предполагает, что вы хотите реализовать IntBinaryOperator.

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

reduce(Math::max);

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

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

15.11.2013

2

:: называется «Справочник по методам». По сути, это ссылка на один метод. т.е. он ссылается на существующий метод по имени.

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

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 прекрасны, потому что :

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

Например. вместо написания тела лямбда

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 в Supplier возвращает значение, но не принимает аргумент. Таким образом, это приводит к ошибке.

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

Еще несколько примеров: метод 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;

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

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

Для получения дополнительной информации см. http://cr.openjdk.java.net/%7Ebriangoetz/lambda/lambda-state-final.html.

07.03.2014
  • Спасибо за объяснение. В итоге: '::' используется для извлечения метода, который удовлетворяет FunctionalInterface (лямбда): ClassX :: staticMethodX или instanceX :: instanceMethodX 13.07.2016

  • 3

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

    Официальную документацию 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);
       }
    }
    
    15.11.2013
  • хорошее объяснение находится здесь: doanduyhai.wordpress.com/2012/07/14/ 15.11.2013
  • @RichardTingle method(Math::max); - это вызов, а определение метода будет похоже на public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}. Вот как его используют. 15.11.2013
  • Для тех, кто знаком с C #, это похоже на DelegateType d = new DelegateType (MethodName); 15.11.2013

  • 4

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

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

    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("Fanishwar", "F"),
                    new Employee("Brian", "B"),
                    new Employee("Donald", "D"),
                    new Employee("Adam", "A"),
                    new Employee("Evan", "E")
                    );
        }
    

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

    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
    

    Согласно документам, существует четыре вида ссылок на методы:

    +----+-------------------------------------------------------+--------------------------------------+
    |    | 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                       |
    +------------------------------------------------------------+--------------------------------------+
    
    21.04.2017
  • Я не понимаю, как compareByGrade, который ожидает два аргумента, может быть вызван так просто, как Employee :: compareByGrade. как узнать, каких двух сотрудников сравнивать? я предполагаю, поскольку он вызывается в sort (), компилятор автоматически выполняет итерацию всех членов массива? как бы вы пошли противоположным путем и указали бы два конкретных объекта, которые хотите сравнить? 16.12.2020
  • @NathanielHoyt Проверьте это stackoverflow.com/questions/ 12386075 / 17.12.2020
  • @NathanielHoyt Стандартный интерфейс списка Javas имеет метод сортировки, который принимает компаратор, который будет использоваться при сортировке списка. Это то, что используется в этом коде. Это было добавлено в Java 8. 07.01.2021

  • 5

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

    Для ссылки на статические методы используется следующий синтаксис:

    ClassName :: methodName 
    

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

    objRef :: methodName
    

    А также

    ClassName :: methodName
    

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

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

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

    05.09.2014
  • Данная ссылка выше больше не работает. 04.12.2020

  • 6

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

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

    Ссылка на метод 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).

    15.11.2013
  • но метод compareByAge не является статическим. 01.03.2016
  • @abbas Также и compareByName. Следовательно, вы получаете доступ к этим нестатическим методам через оператор ссылки с помощью объекта. Если бы они были статическими, вы могли бы использовать имя класса, например ComparisionProvider :: someStaticMethod. 11.06.2017

  • 7

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

    Object :: methodName
    

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

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

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

    Consumer<String> c = System.out::println;
    
    22.03.2017
  • Отличная и простая подсказка! Обратитесь к приему потребителя: c.accept(s); 15.03.2021
  • Спасибо. Простое объяснение / пример, который говорит мне, что я хочу и что мне нужно знать. So:: - это, по сути, еще более короткое лямбда-выражение. Итак, object::nonstaticmethod это ()-> object.nonstaticmethod() или event-> object.nonstaticmethod(). А для class.method() просто class::method. 07.05.2021

  • 8

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

    Purchase::calculatePrice
    

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

    15.11.2016
  • Могу ли я делать ссылки на вложенные методы? например groupingBy (Заказ :: клиент :: имя) 01.03.2017
  • вы не можете сделать ссылку на вложенный метод таким образом 06.11.2019

  • 9

    Мне очень интересен этот источник.

    Фактически, именно лямбда превращается в двойное двоеточие. Двойное двоеточие более читабельно. Мы следуем этим шагам:

    ШАГ1:

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

    ШАГ2:

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

    ШАГ 3:

    // The magic using method reference
    Comparator c = Comparator.comparing(Person::getAge);
    
    04.08.2017
  • Похоже, Person::getAge() должно быть Person::getAge. 06.09.2017

  • 10

    Итак, я вижу здесь массу откровенно чрезмерно сложных ответов, и это еще не все.

    Ответ довольно прост: :: это называется "Ссылки на методы" https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

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


    Теперь давайте кратко рассмотрим, что такое ссылки на методы:

    A :: b несколько заменяет следующее встроенное лямбда-выражение: (params ...) - ›Ab (params ... )

    Чтобы соотнести это с вашими вопросами, необходимо понимать лямбда-выражение Java. Что несложно.

    Встроенное лямбда-выражение похоже на определенный функциональный интерфейс (который представляет собой интерфейс, который имеет не более и не менее 1 метода). Давайте вкратце посмотрим, что я имею в виду:

    InterfaceX f = (x) -> x*x; 
    

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

    InterfaceX может быть любым из этого:

    interface InterfaceX
    {
        public Integer callMe(Integer x);
    }
    

    или это

    interface InterfaceX
    {
        public Double callMe(Integer x);
    }
    

    или более общий:

    interface InterfaceX<T,U>
    {
        public T callMe(U x);
    }
    

    Давайте возьмем первый представленный случай и встроенное лямбда-выражение, которое мы определили ранее.

    До Java 8 вы могли определить это аналогичным образом:

     InterfaceX o = new InterfaceX(){
                         public int callMe (int x) 
                           {
                            return x*x;
                           } };
                         
    

    Функционально это то же самое. Разница больше в том, как компилятор это воспринимает.

    Теперь, когда мы рассмотрели встроенное лямбда-выражение, давайте вернемся к «Ссылки на методы» (: :). Допустим, у вас есть такой класс:

    class Q {
            public static int anyFunction(int x)
                 {
                     return x+5;
                 } 
            }
        
    

    Поскольку метод anyFunctions имеет те же типы, что и InterfaceX callMe, мы можем приравнять эти два типа со ссылкой на метод.

    Мы можем написать это так:

    InterfaceX o =  Q::anyFunction; 
    

    и это эквивалентно этому:

    InterfaceX o = (x) -> Q.anyFunction(x);
    

    Крутая вещь и преимущество ссылок на методы заключается в том, что сначала, пока вы не назначите их переменным, они не имеют типа. Таким образом, вы можете передавать их в качестве параметров в любой эквивалентный функциональный интерфейс (имеющий те же определенные типы). Что и происходит в вашем случае

    07.11.2018

    11

    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 ^_^
    
    23.07.2016

    12

    Поскольку многие ответы здесь хорошо объясняют :: поведение, дополнительно я хотел бы пояснить, что :: оператор не должен иметь точно такую ​​же сигнатуру, что и ссылающийся функциональный интерфейс, если он используется для переменных экземпляра. Предположим, нам нужен 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;
    

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

    public class SubTestObject extends TestObject {
    
        public final TestObject testInstance(TestObject t){
            return t;
        }
    
    }
    

    И звоните:

    BinaryOperator<TestObject> binary = SubTestObject::testInstance;
    

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

    public class TestObject {
    
        public SubTestObject testInstance(Object t){
            return (SubTestObject) t;
        }
    
    }
    
    BinaryOperator<TestObject> binary = TestObject::testInstance;
    

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

    06.03.2017

    13

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

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

    18.09.2017

    14

    В более старых версиях 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();
    }
    
    04.02.2018

    15

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

    Синтаксис:

    <Class name>::<method name>
    

    Этот символ можно использовать вместо ламда-выражений

    Программа:

    // Java code to print the elements of Stream
    // without using double colon operator
      
    import java.util.stream.*;
      
    class MyClass {
        public static void main(String[] args)
        {
      
            // Get the stream
            Stream<String> stream
                = Stream.of("Testing","Program");
      
            // Print the stream
            stream.forEach(s -> System.out.println(s));
        }
    }
    

    Выход:

    Testing
    Program
    

    Строку stream.forEach(s -> System.out.println(s)); можно заменить на

    stream.forEach(System.out::println);
    

    Также при программировании чаще всего используется первый метод.

    12.06.2021

    16

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

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

    08.04.2016

    17

    Предыдущие ответы довольно полны относительно того, что делает ссылка на метод ::. Подводя итог, он предоставляет способ ссылаться на метод (или конструктор) без его выполнения, а при оценке он создает экземпляр функционального интерфейса, который предоставляет контекст целевого типа.

    Ниже приведены два примера поиска объекта с максимальным значением в ArrayList WITH и БЕЗ использования ссылки на метод ::. Пояснения в комментариях ниже.


    БЕЗ использования ::

    import java.util.*;
    
    class MyClass {
        private int val;
        MyClass (int v) { val = v; }
        int getVal() { return val; }
    }
    
    class ByVal implements Comparator<MyClass> {
        // no need to create this class when using method reference
        public int compare(MyClass source, MyClass ref) {
            return source.getVal() - ref.getVal();
        }
    }
    
    public class FindMaxInCol {
        public static void main(String args[]) {
            ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
            myClassList.add(new MyClass(1));
            myClassList.add(new MyClass(0));
            myClassList.add(new MyClass(3));
            myClassList.add(new MyClass(6));
    
            MyClass maxValObj = Collections.max(myClassList, new ByVal());
        }
    }
    

    С использованием ::

    import java.util.*;
    
    class MyClass {
        private int val;
        MyClass (int v) { val = v; }
        int getVal() { return val; }
    }
    
    public class FindMaxInCol {
        static int compareMyClass(MyClass source, MyClass ref) {
            // This static method is compatible with the compare() method defined by Comparator. 
            // So there's no need to explicitly implement and create an instance of Comparator like the first example.
            return source.getVal() - ref.getVal();
        }
    
        public static void main(String args[]) {
            ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
            myClassList.add(new MyClass(1));
            myClassList.add(new MyClass(0));
            myClassList.add(new MyClass(3));
            myClassList.add(new MyClass(6));
    
            MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
        }
    }
    
    15.11.2018

    18

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

    classname :: methodName

    ex:-

    • stream.forEach(element -> System.out.println(element))

    Используя двойное двоеточие ::

    • stream.forEach(System.out::println(element))
    07.02.2019
    Новые материалы

    Очистить файлы Program .cache в Ubuntu 20.10
    Очистите кеш за несколько простых шагов! GNU / Linux реализовал эффективное управление хранилищем для своих пользователей. Но заметили ли вы, что в вашей системе Linux заканчивается место,..

    Использование Node.js для чтения действительно больших файлов (часть 1)
    В этом сообщении в блоге есть интересный источник вдохновения. На прошлой неделе кто-то на одном из моих каналов в Slack опубликовал тестовое задание, которое он получил на должность..

    Введение в градиентный спуск и обратное распространение
    Введение в градиентный спуск и обратное распространение Как машинное обучение? Машинное обучение ( ML ) - это исследование компьютерных алгоритмов, которые автоматически улучшаются по..

    Шаблон CQRS — C#
    Этот архитектурный шаблон в основном говорит о том, что мы можем использовать одну модель для операций чтения, а другую — для операций записи. Звучит хорошо, но реализовать его может быть..

    Освоение функций потерь в машинном обучении для регрессии: полное руководство по оптимизации…
    Введение: Функция потерь является важным компонентом алгоритмов машинного обучения и статистических моделей. Его роль заключается в измерении несоответствия между прогнозируемым выходом модели и..

    10 языков программирования, которые изменят мир в 2023 году
    Мир программирования постоянно развивается, и востребованные языки постоянно меняются. Однако есть несколько языков, которые выдержали испытание временем и по-прежнему очень ценны для изучения в..

    Чем заняться в наших библиотеках (апрель 2023 г.)
    В апреле этого года мы празднуем обучение по-разному — от принятия позитивного и устойчивого мышления до понимания и сохранения местных популяций пчел, а также изучения новейших и новейших..