자바 8 람다와 인터페이스 스펙 변화
람다?
- 코드 블록
- 기존의 코드 블록은 메소드를 -> 메소드를 사용하기 위해 익명 객체를 만들거나 하는 식
- 하지만 자바 8부터는 람다를 이용하여 코드 블록 생성 가능
- 코드 블록을 변수처럼 사용 가능!
함수형 인터페이스를 람다식으로 변경
- 함수형 인터페이스란?
- 추상 메소드를 하나만 갖는 인터페이스
- ex) Runnable 인터페이스 -> run() 추상 메소드 하나만 갖음
- 기존 방식의 코드 블록 사용 : 별도의 클래스와 객체 그리고 메소드를 생성해야 한다.
- 기존의 코드 방법 : 객체 생성 -> 이용
package book;
public class Test1 {
public static void main(String[] args) {
MyTest mt=new MyTest();
Runnable r=mt;
r.run();
}
}
class MyTest implements Runnable {
@Override
public void run() {
System.out.println("Hello Lambda");
}
}
- 익명 객체 생성하고 사용
package book;
public class Test2 {
public static void main(String[] args) {
// 익명 객체 생성
Runnable r =new Runnable() {
@Override
public void run() {
System.out.println("Hello Lambda 2");
}
};
r.run();
}
}
- 익명 객체 없이 람다를 이용하여 코드 블록만 사용
- 가능한 이유?!- Runnable 타입으로 참조 변수 r을 만들고 있기 때문에 굳이 new Runnable()을 사용할 필요 없음.
- run ( ) 메소드 -> ( )로 변경 : 이유는 Runnable 인터페이스가 가진 추상 메소드가 run () 메소드 하나 이기 때문에
package book;
public class Test3 {
public static void main(String[] args) {
Runnable r=() -> {
System.out.println("Hello lambda 3");
};
r.run();
}
}
- 더 짧게?! 코드 안에 들어가는 로직이 1줄이기 때문에 { } 생략가능
package book.chapter01.interfaceFunction;
public class Test4 {
public static void main(String[] args) {
Runnable r =()-> System.out.println("lambda 4");
r.run();
}
}
람다 구조
- 람다의 구조 : (인자 목록) -> {로직}
나만의 인터페이스를 이용해서 람다로 변경
package book.chapter02.myInterfaceFunction;
public class Test1 {
public static void main(String[] args) {
MyFunctionInterface m1=(int a) -> {return a*a;};
int b=m1.runSomething(5);
System.out.println(b);
}
}
@FunctionalInterface
interface MyFunctionInterface {
public abstract int runSomething(int count);
}
- Main 함수에 부분을 더 줄일수 있을까?!
- int가 들어가는 것을 알기 때문에 (인터페이스에서) 따라서 int 생략 가능- 인자 1개가 필요함으로 a를 쓴다!
- runSomething부분에 인자가 int인것을 알기 때문에 return 부분 생략 가능
- 코드가 1줄이기 때문에 { } 생략 가능
package book.chapter02.myInterfaceFunction;
public class Test2 {
public static void main(String[] args) {
MyFunctionInterface mf1=a->a*a;
int b=mf1.runSomething(6);
System.out.println(b);
}
}
메소드 호출 인자 & Return 람다 사용
- 지금까지는 람다식을 함수형 인터페이스의 참조 변수에 저장해서 사용
- 람다식을 변수에 저장하는 것이 가능하다면 당연히 메서드의 인자로도 사용 가능
package book.chapter02.myInterfaceFunction;
public class Test3 {
public static void main(String[] args) {
MyFunctionInterface mf1=a->a*a;
// 메소드에 인자값으로 넣어 사용
doIt(mf1);
}
public static void doIt(MyFunctionInterface mf1) {
int b=mf1.runSomething(5);
System.out.println(b);
}
}
- 람다식을 1번만 사용한다면 참조변수를 만들 필요도 없다!
package book.chapter02.myInterfaceFunction;
public class Test4 {
public static void main(String[] args) {
doIt(a->a*a);
}
public static void doIt(MyFunctionInterface mf1) {
int b=mf1.runSomething(5);
System.out.println(b);
}
}
- 메소드의 반환값으로 람다 사용
package book.chapter02.myInterfaceFunction;
public class Test5 {
public static void main(String[] args) {
MyFunctionInterface mf1=todo();
int result=mf1.runSomething(5);
System.out.println(result);
}
public static MyFunctionInterface todo() {
return num->num*num;
}
}
자바 8 API에서 제공하는 대표적인 함수형 인터페이스
함수형 인터페이스 | 추상 메소드 | 용도 |
---|---|---|
Runnable | void run() | 실행할 수 있는 인터페이스 |
Supplier<T> | T get() | 제공할 수 있는 인터페이스 |
Consumer | void accepnt(T t) | 소비(출력)할수 있는 인터페이스 |
Predeicate<T> | Boolean test(T t) | 입력을 받아 참/거짓을 단정 인터페이스 |
UnaryOperator<T> | T apply(T t) | 단항(Unary)연산을 할 수 있는 인터페이스 |
package book.chapter03.java8FunctionInterface;
import java.util.Arrays;
import java.util.List;
import java.util.function.*;
public class Test1 {
public static void main(String[] args) {
// 실행할수 있는 인터페이스
Runnable run=()-> System.out.println("hello");
run.run();
// 1. 제공할 수 있는 인터페이스
Supplier<Integer>sup=()->3*3;
int m=sup.get();
System.out.println(m);
// 2. 소비할 수 있는 인터페이스 (출력)
Consumer<Integer> con=num -> System.out.println(num);
con.accept(3);
Consumer<String> sCon=s-> System.out.println(s.toUpperCase());
sCon.accept("hello lambda");
// list와 Consumer 같이 사용
Consumer<String>consumer =s-> System.out.println(s.toUpperCase());
List<String> list= Arrays.asList("abc","def","kgh");
list.forEach(consumer);
// 3. 입력을 받아 출력할 수 있는 인터페이스
Function<Integer, String>fun=num->"input: "+num;
String result=fun.apply(10);
System.out.println(result);
// 4. 참/거짓을 판정할 수 있는 인터페이스
Predicate<Integer> pre=num->num>10;
boolean res=pre.test(100);
System.out.println(res);
// 5. 단항 연산을 할 수 있는 인터페이스
UnaryOperator<Integer> uOp=num->num*num;
int k=uOp.apply(100);
System.out.println(k);
}
}
컬렉션 스트림에서 람다 사용
- 람다와 컬렉션을 사용하지 않고 출력하는 방법
package book.chapter03.java8FunctionInterface;
public class Test2 {
public static void main(String[] args) {
Integer []ages={20,25,18,27,30,21,17,19,34,28};
for (int age :ages) {
if (age<20) {
System.out.format("Age %d Can't enter\n",age);
}
}
}
}
- 컬렉션을 스트림을 이용한 방법의 장점
- 선언적 프로그래밍- 의사소통 내용 자체가 그대로 코드로 구현되는 것
- SQL 처럼 where 절과 같은 역할을 수행 가능
package book.chapter03.java8FunctionInterface;
import java.util.Arrays;
public class Test3 {
public static void main(String[] args) {
Integer []ages={20,25,18,27,30,21,17,19,34,28};
Arrays.stream(ages)
// stream에 filter안에는 predicate 함수형 인터페이스가 들어가 있다.
.filter(age->age<20)
// forEach에는 consumer를 요구
.forEach(age->System.out.format("Age %d Can't enter\n",age));
}
}
- Stream은 Collections Stream을 다른 컬렉션 스트림으로 변경 가능
- mapToInt / mapToDouble / mapToLong
- Stream에서 Integer를 int로 변경
- Integer를 double로 변경
- Integer를 Long으로 변경
- sum, count, average, min, max 뿐만 아니라 집계 함수도 사용 가능
package book.chapter03.java8FunctionInterface;
import java.util.Arrays;
public class Test4 {
public static void main(String[] args) {
Integer []ages={20,25,18,27,30,21,17,19,34,28};
// mapToInt -> Integer -> int로 변경
int []test=Arrays.stream(ages).mapToInt(age->age).toArray();
double []dTest=Arrays.stream(ages).mapToDouble(age->age).toArray();
// 출력
Arrays.stream(test).forEach(System.out::println);
// 개수세기
System.out.println(Arrays.stream(ages).count());
// sum, average, min, max
System.out.println(Arrays.stream(ages).mapToInt(age->age).sum());
System.out.println(Arrays.stream(ages).mapToInt(age->age).average());
System.out.println(Arrays.stream(ages).mapToInt(age->age).min());
System.out.println(Arrays.stream(ages).mapToInt(age->age).max());
System.out.println(Arrays.stream(ages).allMatch(age->age>20)); // 모두 조건을 만족하면 true
System.out.println(Arrays.stream(ages).anyMatch(age->age>20)); // 하나라도 만족하면 true
System.out.println(Arrays.stream(ages).findFirst());
System.out.println(Arrays.stream(ages).findAny());
// 정렬된 상태로 출력
Arrays.stream(ages).sorted().forEach(System.out::println);
}
}
메소드 레퍼런스와 생성자 레퍼런스
메소드 레퍼런스란?!
- 간략한 형식으로 변경하여 사용하는것
- 인스턴스::인스턴스메소드
- 클래스::정적메소드
- 클래스::인스턴스멘소드
package book.chapter03.java8FunctionInterface;
import java.util.Arrays;
import java.util.function.BiFunction;
public class Test5 {
public static void main(String[] args) {
Double[]nums={1.0,4.0,9.0,16.0,25.0};
Arrays.stream(nums)
.map(num->Math.sqrt(num))
.forEach(sqrtNum-> System.out.println(sqrtNum));
// Method Reference
Arrays.stream(nums)
.map(Math::sqrt) // 클래스 :: 정적메소드
.forEach(System.out::println); // 인스턴스 :: 인스턴스메소드
BiFunction<Integer, Integer, Integer>bip_lambda=(a,b)->a.compareTo(b);
BiFunction<Integer,Integer,Integer>bip_method_reference=Integer::compareTo; // 클래스:인스턴스메소드
}
}
생성자 레퍼런스
- 클래스::new
package book.chapter03.java8FunctionInterface;
import java.util.function.Supplier;
public class Test6 {
public static void main(String[] args) {
Test6 test=new Test6();
Supplier<Test6> tt=Test6::new;
Test6 test1=tt.get();
Test6 test2=tt.get();
System.out.println(test.hashCode());
System.out.println(test1.hashCode());
System.out.println(test2.hashCode());
}
}
Author And Source
이 문제에 관하여(자바 8 람다와 인터페이스 스펙 변화), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@sds1vrk/자바-8-람다와-인터페이스-스펙-변화저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)