[Java8] Chapter 1-4. 메서드 레퍼런스

 

✍️  메서드 레퍼런스

만약 람다 표현식이 하는 일이 기존에 있는 메서드 또는 생성자를 호출하는 거라면, 메서드 레퍼런스를 사용해서 매우 간결하게 표현할 수 있다.

 

참조하려는 메서드의 형태에 따라 그 방식이 서로 다르다.

 

스태틱 메서드의 참조 방식

  • 타입::스태틱 메서드

특정 객체의 인스턴스 메서드 참조 방식

  • 객체 레퍼런스::인스턴스 메서드

생성자 참조 방식 

  • 타입::new

임의 객체의 인스턴스 메서드 참조 방식 

  • 타입::인스턴스 메서드

 

🍊 메서드 레퍼런스 예제

Person 클래스엔 서로 다른 형태의 메서드들이 존재한다.

인자가 없는 생성자, 인자를 하나 받는 생성자, non-static public 메서드, static 메서드

public class Person {
	private String name;

	// 인자가 없는 생성자
	public Person() {
	}

	// name을 받는 생성자
	public Person(String name) {
		this.name = name;
	}

	// name에게 hello 인사하는 메서드
	public String hello(String name) {
		return "Hello " + name;
	}

	// name에게 hi 인사하는 static 메서드
	public static String hi(String name) {
		return "Hi " + name;
	}
}

 

스태틱 메서드 참조

타입::스태틱 메서드

public static void main(String[] args) {
	{
		Function<String, String> hi = name -> "Hi " + name;
		System.out.println(hi.apply("John"));
	}
	{
		Function<String, String> hi = Person::hi;
		System.out.println(hi.apply("John"));
	}
}

// Hi John
// Hi John

 

 

특정 객체의 인스턴스 메서드 참조

객체 레퍼런스::인스턴스 메서드

public static void main(String[] args) {
	{
		Function<String, String> hello = name -> "Hello " + name;
		System.out.println(hello.apply("John"));
	}
	
	{
		Person person = new Person();
		Function<String, String> hello = person::hello; 
		System.out.println(hello.apply("John"));
	}
}

// Hello John
// Hello John

 

생성자 참조

타입::new

인자가 없는 생성자

public static void main(String[] args) {
	{
		Supplier<Person> personCtor = () -> new Person();
		Person person = personCtor.get();
		System.out.println(person.name);
	}
	{
		Supplier<Person> personCtor = Person::new;
		Person person = personCtor.get();
		System.out.println(person.name);
	}
}

// default
// default

 

인자가 있는 생성자

public static void main(String[] args) {
	{
		Function<String, Person> personCtor = (name) -> new Person(name);
		Person person = personCtor.apply("Kang");
		System.out.println(person.name);
	}
	{
		Function<String, Person> personCtor = Person::new;
		Person person = personCtor.apply("Kang");
		System.out.println(person.name);
	}
}

// Kang
// Kang

재밌는 점은 인자가 없는 생성자, 인자가 있는 생성자 모두 Person::new를 사용한다.

코드상으론 동일하지만 서로 다른 생성자를 사용한다.

 

임의 객체의 인스턴스 메서드 참조

타입::인스턴스 메서드

 

마지막으로 임의 객체의 인스턴스 메서드 참조 방식을 알아볼 텐데 다소 설명이 필요한 부분이 있어서 마지막 파트에서 정리하려 한다.  

 

배열을 정렬함에 있어서 sort 메서드를 사용할 수 있는데, 두 번째 인자로 Comparator를 받는다.

아래처럼 익명 구현 객체를 이용해서 구현할 수도 있다.

String[] names = {"John", "Peter", "Kang"};
Arrays.sort(names, new Comparator<String>() {

    @Override
    public int compare(String o1, String o2) {
        // TODO Auto-generated method stub
        return 0;
    }
});

 

자바 8부턴 Comparator가 함수형 인터페이스로 바뀌게 되면서 익명 구현 객체 대신 람다 표현식을 사용할 수 있게 됐고 람다 표현식을 사용할 수 있으니 메서드 레퍼런스도 사용할 수 있다.

 

어쨌든, 다시 본론으로 와서 메서드 레퍼런스를 사용하면 다음과 같이 코드를 작성할 수 있다.

String[] names = {"John", "Peter", "Kang"};
Arrays.sort(names, String::compareToIgnoreCase); // 대소문자 구분없이 String 비교

 

그런데 조금 이상한 부분이 있다. Comparator의 추상 메서드인 compare는 두 개의 인자를 받는데, compareToIgnoreCase는 한 개의 인자만 받는다. 어째서 가능한 걸까? 

public int compareToIgnoreCase(String str) {
    return CASE_INSENSITIVE_ORDER.compare(this, str);
}

 

임의 객체의 인스턴스 메서드를 참조하는 방식엔 약간의 특이점이 존재할 수밖에 없는데, 말 그대로 "임의" 객체의 인스턴스 메서드를 참조하므로 특정 객체를 외부에서 받아와야 한다. 

 

예를 들어

메서드 레퍼런스로 다음과 같이 코드를 작성하면 String::compareToIgnoreCase는 두 개의 인자를 받는데, 첫 번째 인자가 메서드를 호출할 객체가 되고 두 번째 인자는 호출한 메서드에 넘겨질 매개변수가 된다. 

쉽게 말하면 String::compareToIgnoreCase는 (x, y) -> x.compareToIgnoreCase(y)와 동일하다.

 

 

본 내용은 백기선님의 자바8 강의 내용입니다.

 

더 자바, Java 8 - 인프런 | 강의

자바 8에 추가된 기능들은 자바가 제공하는 API는 물론이고 스프링 같은 제 3의 라이브러리 및 프레임워크에서도 널리 사용되고 있습니다. 이 시대의 자바 개발자라면 반드시 알아야 합니다. 이

www.inflearn.com