:: (подвійна двокрапка) на 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
Синтаксичним цукром є компілятор, який автоматично генерує реалізацію інтерфейсу на основі функції, яку ви надаєте (щоб полегшити використання всього лямбда з існуючими базами коду).
Неет

4
java.dzone.com/articles/java-lambda-expressions-vs може допомогти, не заглиблюючись у тему
Pontus Backlund

8
@Neet це не зовсім "синтаксичний цукор", якщо ви не можете сказати для чого. тобто "х - синтаксичний цукор для у".
Інго

6
@Ingo створює новий об’єкт лямбда кожного разу, коли я його використовую. TestingLambda$$Lambda$2/8460669і TestingLambda$$Lambda$3/11043253були створені на двох викликах.
Нарендра Патай

13
Лямбда і посилання на методи не є "простими старими анонімними внутрішніми класами". Дивіться програмісти.stackexchange.com/a/ 181743/ 59134 . Так, у разі необхідності нові класи та екземпляри створюються на ходу, якщо це необхідно, але лише за потреби.
Стюарт відзначає

Відповіді:


1022

Зазвичай 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реалізації вище!

(Див. Також Переклад лямбдаських виразів )


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. Він бере один аргумент і дає результат.


Причина, чому ::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очікує аргументу та повертає a 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не бере аргументів і повертає a 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 .


6
Дякую за пояснення. Підсумовуючи: '::' використання для вилучення методу, який задовольняє функціональний інтерфейс (лямбда): ClassX :: staticMethodX, або instanceX :: instanceMethodX "
jessarah

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));}. Ось як його використовували.
Narendra Pathai

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

де getDummyE Employee () - такий метод, як:

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")
                );
    }

Тепер ми знаємо, що компаратор є функціональним інтерфейсом. Функціональний інтерфейс є один рівно один абстрактний метод (хоча він може містити один або кілька методів по замовчуванням або статичні). Вираз лямбда забезпечує реалізацію, @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                       |
+------------------------------------------------------------+--------------------------------------+

25

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

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

ClassName :: methodName 

Для посилання на нестатичні методи синтаксис є

objRef :: methodName

І

ClassName :: methodName

Єдиною умовою посилання на метод є те, що метод існує у функціональному інтерфейсі, який повинен бути сумісний із посиланням на метод.

Посилання на методи при оцінці створюють екземпляр функціонального інтерфейсу.

Знайдено на веб-сайті : http://www.speakingcs.com/2014/08/method-references-in-java-8.html


22

Це посилання на метод у Java 8. Документація на oracle тут .

Як зазначено в документації ...

Посилання на метод Person :: сравнениеByAge - це посилання на статичний метод.

Далі наведено приклад посилання на метод примірника певного об'єкта:

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

Посилання методу myComppareProvider :: сравнениеByName викликає метод CompareByName, який є частиною об'єкта myComppareProvider. JRE виводить аргументи типу методу, які в цьому випадку є (Person, Person).


2
але метод 'сравнениеByAge' не є статичним.
абас

3
@abbas Ні - це порівнянняByName. Отже, ви отримуєте доступ до цих нестатичних методів через опорний оператор, використовуючи об'єкт. Якби вони були статичними, ви можете використовувати назву класу на зразок ComparisionProvider :: someStaticMethod
Seshadri R

6

:: Оператор був представлений в java 8 для посилання на метод. Посилання на метод - це скорочений синтаксис лямбда-виразу, який виконує лише ОДИН метод. Ось загальний синтаксис посилання на метод:

Object :: methodName

Ми знаємо, що ми можемо використовувати лямбда-вирази замість анонімного класу. Але іноді лямбда-вираз справді є лише закликом до якогось методу, наприклад:

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

Щоб зробити код більш зрозумілим, ви можете перетворити цей лямбда-вираз у посилання на метод:

Consumer<String> c = System.out::println;

3

:: відомий як посилання на метод. Скажімо, ми хочемо викликати метод CalcuPrice класу Purchase. Тоді ми можемо записати це як:

Purchase::calculatePrice

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


Чи можу я зробити посилання на вкладені методи? наприклад, groupingBy (Замовлення :: клієнт :: ім'я)

таким чином ви не можете зробити посилання на вкладений метод
Кірбі

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;

Він також не буде компілюватися, компілятор все одно шукатиме статичну довідку. Але нижче код буде складати чудово, оскільки він проходить це тест:

public class TestObject {

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

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

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


2

У редукторі потоків java-8 у простих роботах - це функція, яка приймає два значення як вхідні дані та повертає результат після деякого обчислення. цей результат подається при наступній ітерації.

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


1

Під час виконання вони поводяться абсолютно однаково. Байт-код може / не бути однаковим (для вищевикладеного Incase, він генерує той самий байт-код (виконайте вище та перевірте javaap -c;))

Під час виконання вони поводяться абсолютно однаково. Метод (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

Тож я бачу тут безліч відповідей, відверто ускладнених, і це заниження.

Відповідь досить проста: :: називається Методичні посилання https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Тож я не скопіюю та вставте, за посиланням ви можете знайти всю інформацію, якщо прокрутити до таблиці.


Тепер давайте коротко розглянемо, що таке посилання на метод:

A :: B дещо підміняє наступний вбудований лямбда-вираз : (params ...) -> AB (params ...)

Щоб співвіднести це з вашими питаннями, необхідно зрозуміти вираз java lambda. Що не важко.

Вбудований лямбда-вираз схожий на визначений функціональний інтерфейс (це інтерфейс, який не має більше і не менше 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;
                       } };

Функціонально - це те саме. Різниця полягає більше в тому, як компілятор сприймає це.

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

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

Оскільки метод anyFunctions має ті самі типи, що й InterfaceX callMe , ми можемо еквівалентно ці два з посиланням на метод.

Ми можемо написати це так:

InterfaceX o =  Q::anyFunction; 

і це рівнозначно цьому:

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

Класна річ і перевага Методичних посилань полягає в тому, що спочатку, поки ви не призначите їх змінним, вони не мають типу. Таким чином, ви можете передати їх як параметри до будь-якого еквівалентного вигляду (має однакові визначені типи) функціонального інтерфейсу. Саме це і відбувається у вашому випадку


1

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

Нижче наведено два приклади пошуку об’єкта з максимальним значенням в ArrayListСЕКТОРІ та БЕЗ використання ::посилання методу. Пояснення - у коментарях нижче.


БЕЗ використання ::

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 як посилання на метод . Посилання на метод - це форма лямбда-виразу, яка використовується для позначення існуючого методу за його назвою.

classname :: methodName

колишній: -

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

За допомогою подвійної кишки ::

  • stream.forEach(System.out::println(element))
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.