:: 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에 대한 구문 설탕이다".
Ingo

6
@ Ingo는 사용할 때마다 람다의 새로운 객체를 만듭니다. TestingLambda$$Lambda$2/8460669그리고 TestingLambda$$Lambda$3/11043253두 호출에 만들어졌다.
Narendra Pathai

13
람다 및 메서드 참조는 "일반 익명의 내부 클래스"가 아닙니다. programmers.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 컴파일러는 "감지" int합니다 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인수를 기대하고를 반환합니다 double. 공급 업체get메소드 는 값을 반환하지만 인수를받지 않습니다. 따라서 오류가 발생합니다.

메소드 참조는 기능 인터페이스의 메소드를 나타냅니다. 언급했듯이 기능 인터페이스에는 각각 하나의 방법 만있을 수 있습니다.

더 많은 예 : Consumeraccept메소드 는 입력을 받지만 아무것도 반환하지 않습니다.

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을 참조 하십시오 .


6
설명 감사합니다. 요약 : '::'FunctionalInterface (lambda)를 만족하는 메소드를 추출하는 데 사용합니다. ClassX :: staticMethodX 또는 instanceX :: instanceMethodX "
jessarah

55

응 그것은 사실이야. ::연산자 방법 - 참조에 사용된다. 따라서 클래스의 정적 메소드 또는 객체의 메소드를 사용하여 정적 메소드를 추출 할 수 있습니다 . 생성자에도 동일한 연산자를 사용할 수 있습니다. 여기에 언급 된 모든 사례는 아래 코드 샘플에 예시되어 있습니다.

오라클의 공식 문서는 여기 에서 찾을 수 있습니다 .

기사 에서 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);
Adrian Zanescu

27

그것은 조금 늦게 보이지만 여기에 내 2 센트가 있습니다. 람다 표현식은 익명 메소드를 만드는 데 사용됩니다. 기존 메소드를 호출하는 것 외에는 이름을 사용하지 않고 메소드를 직접 참조하는 것이 더 명확합니다. 그리고 메소드 참조 는 메소드 참조 연산자를 사용하여 그렇게 할 수 있습니다.:: .

각 직원의 이름과 등급이있는 다음과 같은 간단한 수업을 고려하십시오.

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

이제 우리는 비교기 가 기능적 인터페이스 라는 것을 알고 있습니다. 기능 인터페이스 (이 하나 이상의 기본 또는 정적 방법을 포함 할 수 있지만) 정확하게 하나의 추상 메소드를 사용하여 하나입니다. Lambda 표현식은 @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 :: 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은 오브젝트 myComparisonProvider의 일부인 compareByName 메소드를 호출합니다. JRE는이 경우 (Person, Person) 인 메소드 유형 인수를 유추합니다.


2
그러나 'compareByAge'메소드는 정적이 아닙니다.
abbas

3
@abbas Nor는 compareByName입니다. 따라서 객체를 사용하여 참조 연산자를 통해 이러한 비 정적 메서드에 액세스합니다. 그들은 정적 인 경우에, 당신은 ComparisionProvider :: someStaticMethod 같은 클래스 이름을 사용할 수 있습니다
Seshadri R

6

:: 메소드 참조를 위해 연산자 가 java 8에 도입되었습니다. 메서드 참조는 ONE 메서드 만 실행하는 람다 식의 간단한 구문입니다. 메소드 참조의 일반적인 구문은 다음과 같습니다.

Object :: methodName

익명 클래스를 사용하는 대신 람다 식을 사용할 수 있다는 것을 알고 있습니다 . 그러나 때로는 람다 식은 실제로 일부 메서드를 호출하는 것입니다.

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

코드를 명확하게하기 위해 해당 람다 식을 메서드 참조로 변환 할 수 있습니다.

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

3

::는 메소드 참조로 알려져 있습니다. Purchase 클래스의 calculatePrice 메소드를 호출한다고 가정 해 봅시다. 그런 다음 다음과 같이 작성할 수 있습니다.

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);이다 NOT EQUAL은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

여기에 많은 답변이 잘 설명되어 있으므로 ::추가로 :: 연산자가 인스턴스 변수에 사용되는 경우 참조 기능 인터페이스와 정확히 동일한 서명을 가질 필요가 없음 을 분명히하고 싶습니다 . TestObject 유형의 BinaryOperator 가 필요하다고 가정합니다 . 전통적인 방식으로 다음과 같이 구현됩니다.

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;

내 식은 "TestObject 유형에서 정적이 아닌 메소드 testInstance (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)를 호출 할만큼 똑똑합니다. testInstanceTestObject가 아닌 다른 리소스에서 사용할 수 있습니까?

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;

* 나는 단지 공부하고 있으므로 시도하고보고 알아 냈습니다. 잘못되면 나를 바로 고쳐주세요.


2

간단한 작업에서 Java-8 Streams Reducer는 두 개의 값을 입력으로 사용하고 계산 후 결과를 반환하는 함수입니다. 이 결과는 다음 반복에서 제공됩니다.

Math : max 함수의 경우, 메소드는 전달 된 최대 2 개의 값을 계속 반환하며 결국에는 가장 큰 수를 갖습니다.


1

런타임에 그들은 정확히 동일하게 동작합니다.

런타임에 그들은 똑같은 동작을합니다. 메소드 (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

따라서 복사하여 붙여 넣기하지 않습니다. 링크에서 테이블로 스크롤하면 모든 정보를 찾을 수 있습니다.


이제 Method References가 무엇인지 간단히 살펴 보자.

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

기능적으로도 마찬가지입니다. 차이점은 컴파일러가 이것을 어떻게 인식하는지에 있습니다.

이제 인라인 람다 식을 살펴 보았으니, Method References (: :)로 돌아 갑시다. 다음과 같은 수업이 있다고 가정 해 봅시다.

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

anyFunctions 메소드 는 InterfaceX callMe 와 유형이 동일 하므로 두 가지를 메소드 참조와 동등하게 만들 수 있습니다.

우리는 다음과 같이 쓸 수 있습니다 :

InterfaceX o =  Q::anyFunction; 

그리고 이것은 다음과 같습니다.

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

Method References의 멋진 점과 장점은 처음에 변수에 변수를 할당 할 때까지 유형이 없다는 것입니다. 따라서 매개 변수로 동등한 모양 (정의 된 유형이 있음) 기능 인터페이스에 매개 변수로 전달할 수 있습니다. 귀하의 경우에 정확히 무슨 일이 일어나는가


1

이전 답변은 무엇에 관한 것입니까? :: 메소드 참조의 기능 합니다. 요약하면 메소드 (또는 생성자)를 실행하지 않고 참조하는 방법을 제공하며 평가되면 대상 유형 컨텍스트를 제공하는 기능 인터페이스의 인스턴스를 작성합니다.

다음은 ArrayListWITH 및 WITHOUT ::메서드 참조를 사용하지 않고 max 값을 가진 객체를 찾는 두 가지 예 입니다. 설명은 아래 주석에 있습니다.


사용하지 않고 ::

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

이중 콜론을 사용하여 ::

  • stream.forEach(System.out::println(element))
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.