32일차
9. 중첩선언과 익명객체
익명객체 가장 중요함.
주의할점?
클래스 상속과 정의를 한번에 하면 이름을 정하지 않아도 사용할 수 있다.
9.6 중첩 인터페이스
인터페이스는 혼자 인스턴스를 만드는게 불가능하고
구현클래스를 만들고 인스턴스를 만들어야한다.
익명객체를 사용하면 이 과정을 생략할 수 있다.
interface MyInterface08 {
void method1();
}public class C08Anonymous {
public static void main(String[] args) {
MyInterface08 o1 = new MyInterface08() {
@Override
public void method1() {
System.out.println("메소드 재정의!!!");
}
};
o1.method1();
}
}9.7 FinalVariable
로컬클래스는 감싸고 있는 메소드의 지역변수(파라미터를) 사용할 수 있다.
이때 이 지역변수(파라미터)는 final이어야 한다.
지역클래스 안에서 감싸고있는 메소드의 지역변수를 사용하면 fianl이어야한다.
final을 붙이지 않아도 묵시적으로 final이된다.
값을 재정의하면 로컬클래스에서 사용할 수 없게 된다.
public static void main(String[] args) {
final int i = 10;
// i = 11; 불가능
class LocalClass {
void method1() {
System.out.println(i);
}
}
}public void method1() {
int i = 1; // effectively(묵시적) final임
i = 2;
class LocalClass {
void method2() {
//System.out.println(i); 사용불가됨
}
}
}파라미터를 사용하더라도 fianl값이 된다.
메소드를 호출할때 받은 값이 사실상 final의 값이 된다.
public void method3(int param) {
class LocalClass {
void method4() {
System.out.println(param);
}
}
}이러한 규칙은 '지역변수'에만 적용되는 것이다. 필드는 값이 변경되어도 상관없다.
public class C10FinalVariable {
int j;
public void method1() {
int i = 3;
j = 99;// ok
j = 100; // ok
class LocalClass {
void method2() {
System.out.println(i);
System.out.println(j);
}
}
}
}16. 람다식
익명구현객체를 구현할때 많은 것을 생략할 수 있다.
interface MyInterface02 {
void method1();
}public class C02Lambda {
public static void main(String[] args) {
MyInterface02 o1 = () -> System.out.println("재정의한 메소드");
o1.method1();
}
}16.1 람다식이란?
메소드 이름이 빠졌는데 어떻게 그 메소드를 재정의했는지 알수있는가?
메소드가 하나만 있으면 그 메소드를 지정하는 지 알 수 있다.
그래서 추상메소드가 하나인 interface의 익명클래스 객체를 만드는 코드가 람다식이다.
functional interface : 추상 메소드가 하나인 interface이다.
@FunctionalInterface 어노테이션을 붙이면 확인할 수 있다.
추상메소드만 하나 있으면되고 다른 메소드는 존재해도 상관없다.
그런데 Object에 있는 메소드는 추상메소드 수에 포함 안된다.
모든 클래스는 Object를 상속받기 때문에 이미 Object에 재정의 되어 있으니 상관없는 것이다.
@FunctionalInterface
interface MyInterface01{
void method1();
// Object에 있는 메소드는 추상메소드 수에 포함 안된다.
String toString();
default void method2() {
}
static void method3() {
}
}public class C01Lambda {
public static void main(String[] args) {
//functional interface로 lambda식만들 수 있음
MyInterface01 o1 = () -> System.out.println("재정의 메소드@");
MyInterface01 o2 = () -> System.out.println("또다른 메소드@");
o1.method1();
o2.method1();
}
}16.2 파라미터가 없는 람다식
람다식은 파라미터 목록 -> 메소드 body의 형태이다.
메소드의 명령문이 하나이면 중괄호 생략가능하다.
if문이나 for문 같은 것임. 이전은 왠만하면 생략안햇었다.
람다식에서는 생략하는 것이 가독성이 더 좋으니 가능하다면 생략하자.
public class C02Lambda {
public static void main(String[] args) {
MyInterface02 o2 = () -> {
System.out.println("명령문 여러개1");
System.out.println("명령문 여러개2");
};
//생략가능하면 생략!!
//lambda는 생략할수록 읽기 쉬움
MyInterface02 o3 = () -> System.out.println("명령문 하나");
}
}16.3 파라미터가 하나 있는 람다식
파라미터를 ()빈괄호 안에 넣으면된다.
이때 파라미터의 타입이 이미 추상메소드에 명시되어 있으니 타입도 생략이 가능하다.
파라미터가 하나라면 괄호를 생략해도 된다.
파라미터가 추상메소드의 파라미터와 이름이같을 필요는 없다.
public class C03Lambda {
public static void main(String[] args) {
MyInterface03 o2 = (int a) -> {
System.out.println("익명 클래스로 재정의");
System.out.println("받은 값: " + a);
};
//타입 생략
MyInterface03 o3 = (a) -> {
System.out.println("익명 클래스로 재정의");
System.out.println("받은 값: " + a);
};
//괄호 생략
MyInterface03 o4 = a -> {
System.out.println("괄호 생략");
System.out.println("받은 값: " + a);
};
//파라미터이름이 같을 필요 없다.
MyInterface03 o5 = x -> {
System.out.println("괄호 생략");
System.out.println("받은 값: " + x);
};
}
}16.3.2 파라미터가 여러개 있는 람다식
역시나 타입은 생략이 가능하고 파라미터 이름은 상관없다.
public class C04Lambda {
public static void main(String[] args) {
MyInterface04 o1 = new MyInterface04() {
@Override
public void method1(int x, int y) {
System.out.println("익명클래스");
System.out.println(x + ", " + y);
}
};
MyInterface04 o2 = (int x, int y) -> {
System.out.println("람다식");
System.out.println(x + ", " + y);
};
MyInterface04 o3 = (a, b) -> {
System.out.println("타입 생략");
System.out.println(a + ", " + b);
};
}
}16.4 리턴이 있을 때 람다식
문법은 아예 새로운 람다식이지만 결국은 추상클래스가 하나인 인터페이스의 익명구현객체를 만드는 문법이다.
추상메소드가 리턴값이 있다면 타입이 일치하는 리턴값을 꼭만나야한다.
명령문 return문 하나면 중괄호와 return 생략가능하다.
public class C05Lambda {
public static void main(String[] args) {
MyInterface05 o2 = () -> {
System.out.println("람다로 메소드 재정의");
return 3;
};
//명령문 return 하나면 중괄호와 return 생략가능
MyInterface05 o3 = () -> 5;
}
}16.4.1 확인문제 6번
function 함수형 인터페이스를 작성해보세요
public class Example {
public static void main(String[] args) {
double result = calc((x, y) -> (x / y));
System.out.println("result: " + result);
}
public static double calc(Function fun) {
double x = 10;
double y = 4;
return fun.apply(x, y);
}
}@FunctionalInterface
interface Function {
double apply(double x, double y);
}16.4.2 확인문제 7번
배열 항목 중에서 최대값 또는 최소값을 찾는 코드
public class Example {
private static int[] scores = { 10, 50, 3 };
public static int maxOrMin(Operator operator) {
int result = scores[0];
for (int score : scores) {
result = operator.apply(result, score);
}
return result;
}
public static void main(String[] args) {
int max = maxOrMin((x, y)->Math.max(x, y));
System.out.println("최대값: " + max);
int min = maxOrMin((x, y)->Math.min(x, y));
System.out.println("최소값: " + min);
}
}16.4.3 확인문제 8번
학생의 영어 평균점수와 수학평균 점수 계산하기
public interface Function<T> {
public double apply(T t);
}public class Example {
private static Student[] students = {
new Student("홍길동", 90, 96),
new Student("신용권", 95, 93)
};
//avg()메소드 작성
public static double avg(Function<Student> function) {
double sum = 0;
for (int i = 0; i < students.length; i++) {
sum += function.apply(students[i]);
}
double avg = sum / students.length;
return avg;
}
public static void main(String[] args) {
double engAvg = avg(s -> s.getEngScore());
System.out.println("영어 평균 점수: " + engAvg);
double mathAvg = avg(s -> s.getMathScore());
System.out.println("수학 평균 점수: " + mathAvg);
}
}16.5 메소드 참조
인터페이스의 추상메소드가 값을 받아서 어떤 메소드에 값을 전달하는 일만할때 사용한다.
16.5.1 스태틱 메소드 참조
스태틱메소드의 경우 파라미터가 아예 생략되고 클래스명::메소드명하면된다.
파라미터의 순서가 맞기만 하면된다.
@FunctionalInterface
interface MyInterface01 {
void action(int a);
}class MyClass01 {
public static void method(int a) {
}
}public class C01MethodReference {
public static void main(String[] args) {
MyInterface01 o1 = a -> System.out.println(a);
MyInterface01 o2 = a -> MyClass01.method(a);
//MethodReference (스태틱 메소드 참조)
MyInterface01 o3 = MyClass01::method;
}
}메소드참조는 항상 생략하는게 아니라 단순히 호출일때만 가능하다.
파라미터 수가같고 순서가 같을때 생략 가능하다.
16.5.2 인스턴스 메소드 참조
인스턴스 객체생성하고 변수이름::메소드하면 된다.
interface MyInterface04 {
void action(int a);
}class MyClass03 {
void method1() {
}
void method2(int a, int b) {
}
public void method(int a) {
}
}public class C03MethodReference {
public static void main(String[] args) {
MyInterface04 o1 = a -> System.out.println(a);
MyClass03 s1 = new MyClass03();
MyInterface04 o2 = a -> s1.method(a);
MyInterface04 o3 = s1::method;
MyInterface02 o4 = () -> s1.method1();
MyInterface02 o5 = s1::method1;
MyInterface03 o6 = (a,b) -> s1.method2(a, b);
MyInterface03 o7 = s1::method2;
}
}2023.03.13 후기
람다식을 일부러 활용해서 스레드와 같이 익명구현객체를 만들때 사용하도록 노력은 했었으나 생각보다 사용할 일이 적어 사용할 생각을 하는 것이 힘든 것 같다. 개념이나 내용자체는 어렵지 않기때문에 사용에는 무리가 없지만 기억하고 사용하는 것이 쉽지않다.
람다식 헷갈렷던 참조 부분을 강사님께 여쭤보며 진행할 수 있게 되었다.
메소드참조부분을 왜 사용하는지 이해를 잘 못했었는데 인터페이스의 추상메소드가 값을 받아서 단순히 어떤 메소드에 값을 전달하는 일만할때 사용한다는 점을 다시 알고보니 이해가 된다.
내일 생성자 참조를 나가며 이해가 거의 안됬던 부분을 더 이해해보고자 한다.
'국비 > Java' 카테고리의 다른 글
| 2023.03.15 34일차 Java (0) | 2023.03.15 |
|---|---|
| 2023.03.14 33일차 Java (0) | 2023.03.14 |
| 2023.03.10 31일차 Java (0) | 2023.03.10 |
| 2023.03.09 30일차 Java (0) | 2023.03.09 |
| 2023.03.08 29일차 Java (0) | 2023.03.08 |