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
됩니까?
답변
일반적 으로 다음을 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
위 의 구현 의 짧은 (그리고 아마도 더 효율적인) 버전으로 간주 될 수 있습니다 !
( 람다 식 번역 참조 )
답변
::
메소드 참조라고합니다. 기본적으로 단일 방법에 대한 참조입니다. 즉, 이름으로 기존 방법을 나타냅니다.
간단한 설명 :
아래는 정적 메소드에 대한 참조 예입니다.
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
메소드 는 값을 반환하지만 인수를받지 않습니다. 따라서 오류가 발생합니다.
메소드 참조는 기능 인터페이스의 메소드를 나타냅니다. 언급했듯이 기능 인터페이스에는 각각 하나의 방법 만있을 수 있습니다.
더 많은 예 : Consumer 의 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;
메소드 참조는 다른 스타일을 가질 수 있지만 기본적으로 모두 동일한 것을 의미하며 간단히 람다로 시각화 할 수 있습니다.
- 정적 방법 (
ClassName::methName
) - 특정 객체의 인스턴스 메소드 (
instanceRef::methName
) - 특정 객체의 슈퍼 방법 (
super::methName
) - 특정 유형의 임의 객체의 인스턴스 메소드 (
ClassName::methName
) - 클래스 생성자 참조 (
ClassName::new
) - 배열 생성자 참조 (
TypeName[]::new
)
추가 참조는 http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html을 참조 하십시오 .
답변
응 그것은 사실이야. ::
연산자 방법 – 참조에 사용된다. 따라서 클래스의 정적 메소드 또는 객체의 메소드를 사용하여 정적 메소드를 추출 할 수 있습니다 . 생성자에도 동일한 연산자를 사용할 수 있습니다. 여기에 언급 된 모든 사례는 아래 코드 샘플에 예시되어 있습니다.
이 기사 에서 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);
}
}
답변
그것은 조금 늦게 보이지만 여기에 내 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 |
+------------------------------------------------------------+--------------------------------------+
답변
::
Java 8에 포함 된 새로운 연산자로 기존 클래스의 메소드를 참조하는 데 사용됩니다. 클래스의 정적 메서드와 비 정적 메서드를 참조 할 수 있습니다.
정적 메소드를 참조하기위한 구문은 다음과 같습니다.
ClassName :: methodName
비 정적 메소드를 참조하기 위해 구문은 다음과 같습니다.
objRef :: methodName
과
ClassName :: methodName
메소드를 참조하기위한 유일한 전제 조건은 메소드가 기능 인터페이스에 존재하고 메소드 참조와 호환되어야한다는 것입니다.
메소드 참조는 평가 될 때 기능 인터페이스의 인스턴스를 작성합니다.
http://www.speakingcs.com/2014/08/method-references-in-java-8.html 에 있습니다.
답변
이것은 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) 인 메소드 유형 인수를 유추합니다.
답변
:: 메소드 참조를 위해 연산자 가 java 8에 도입되었습니다. 메서드 참조는 ONE 메서드 만 실행하는 람다 식의 간단한 구문입니다. 메소드 참조의 일반적인 구문은 다음과 같습니다.
Object :: methodName
익명 클래스를 사용하는 대신 람다 식을 사용할 수 있다는 것을 알고 있습니다 . 그러나 때로는 람다 식은 실제로 일부 메서드를 호출하는 것입니다.
Consumer<String> c = s -> System.out.println(s);
코드를 명확하게하기 위해 해당 람다 식을 메서드 참조로 변환 할 수 있습니다.
Consumer<String> c = System.out::println;