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

956

Я изучал исходный код 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?

Нарендра Патхай
источник
60
Синтаксический сахар - это иметь автоматическую генерацию реализаций интерфейса компилятором на основе предоставляемой вами функции (чтобы облегчить использование всей лямбды с существующими базами кода).
Neet
4
java.dzone.com/articles/java-lambda-expressions-vs может помочь, не углубляясь в тему
Pontus Backlund
8
@ Нет, это не совсем "синтаксический сахар", если только вы не можете сказать за что. то есть "x является синтаксическим сахаром для y".
Инго
6
@ Инго он создает новый объект лямбда каждый раз, когда я его использую. TestingLambda$$Lambda$2/8460669и TestingLambda$$Lambda$3/11043253были созданы на двух вызовах.
Нарендра Патхай
13
Лямбды и ссылки на методы не являются «простыми старыми анонимными внутренними классами». См. Programmers.stackexchange.com/a/181743/59134 . Да, если необходимо, новые классы и экземпляры создаются на лету, при необходимости, но только при необходимости.
Стюарт Маркс

Ответы:

1024

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

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

reduce(Math::max);

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

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

isnot2bad
источник
489

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

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

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. Он принимает один аргумент и дает результат.


Причина, по которой ::это здорово, заключается в том, что :

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

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

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 принимает входные данные, но ничего не возвращает.

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/~briangoetz/lambda/lambda-state-final.html .

Jatin
источник
6
Спасибо за объяснение. В итоге: '::' используется для извлечения метода, который удовлетворяет FunctionalInterface (lambda): ClassX :: staticMethodX или instanceX :: instanceMethodX "
Джессара
55

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

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

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

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

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                       |
+------------------------------------------------------------+--------------------------------------+
akhil_mittal
источник
25

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

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

ClassName :: methodName 

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

objRef :: methodName

А также

ClassName :: methodName

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

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

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

Сринат
источник
22

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

david99world
источник
2
но метод «CompareByAge» не является статическим.
Аббас
3
@abbas Также не сравнитьByName. Следовательно, вы получаете доступ к этим нестатическим методам через оператор ссылки, используя объект. Если бы они были статичными, вы могли бы использовать имя класса, например, ComparisionProvider :: someStaticMethod
R
6

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

Object :: methodName

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

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

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

Consumer<String> c = System.out::println;
Vaibhav9518
источник
3

:: известен как ссылки на метод. Допустим, мы хотим вызвать метод Calculate для класса «Покупка». Тогда мы можем написать это как:

Purchase::calculatePrice

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

Сону
источник
Могу ли я сделать ссылки на вложенные методы? например, groupingBy (Order :: customer :: name)
Вы не можете сделать ссылку на вложенный метод таким способом
Кирби
3

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

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

ШАГ 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);
Хусем Бадри
источник
3
Кажется, Person::getAge()должно быть Person::getAge.
Qwertiy
2

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

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

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

HRgiger
источник
2

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

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

Pramod
источник
1

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

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

Альфа Хатун
источник
1

В старых версиях 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

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

Ответ довольно прост: :: он называется Method References 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, int y) 
                       {
                        return x*x;
                       } };

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

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

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

Так как метод anyFunctions имеет те же типы, что и интерфейсный интерфейс CallMe , мы можем приравнять эти два с помощью метода Reference.

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

InterfaceX o =  Q::anyFunction; 

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

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

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

Нертан Люциан
источник
1

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

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


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

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);
    }
}
Лютонг Чен
источник
-1

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

имя_класса :: имяМетода

например: -

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

Используя Double Colon ::

  • stream.forEach(System.out::println(element))
Ишант Кулшрештха
источник