- JAVA
- 내부 클래스(inner class)
- 클래스 안에 선언된 클래스
- 특정 클래스 내에서만 주로 사용되는 클래스를 내부 클래스로 선언한다
- GUI어플리케이션(AWT, Swing)의 이벤트처리에 주로 사용된다.
- 내부 클래스는 외부 클래스의 멤버와 같이 간주되므로, static, private, protected, abstract, final 등의 제어자도 사용이 가능하다.
package ex1;
class OuterClass {
private String myName;
private int num;
OuterClass(String name) {
myName = name;
num = 0;
}
public void whoAreYou() {
num++;
System.out.println(myName + " OuterClass " + num);
}
//내부(Inner) 클래스
//outer가 만들어진 후에 만들어질 수 있다.
class InnerClass {
InnerClass() {
whoAreYou();
}
}
}
class InnerClassTest {
public static void main(String[] args) {
OuterClass out1=new OuterClass("First");
OuterClass out2=new OuterClass("Second");
out1.whoAreYou();
out2.whoAreYou();
OuterClass.InnerClass inn1=out1.new InnerClass();
OuterClass.InnerClass inn2=out2.new InnerClass();
OuterClass.InnerClass inn3=out1.new InnerClass();
OuterClass.InnerClass inn4=out1.new InnerClass();
OuterClass.InnerClass inn5=out2.new InnerClass();
}
}
- Local 클래스(Inner 클래스의 성격과 동일)
- Outer 클래스의 인스턴스 생성 후에야 Inner 클래스의 인스턴스 생성이 가능하다.
- Inner 클래스 내에서는 Outer 클래스의 멤버에 직접 접근이 가능하다.
- Inner 클래스의 인스턴스는 자신이 속할 Outer 클래스의 인스턴스를 기반으로 생성된다.
- Local 클래스는 메소드 내에 정의가 되어서, 메소드 내에서만 인스턴스의 생성 및 참조변수의 선언 이 가능하다는 특징이 있다.
package ex1;
interface Readable {
public void read(); // 필수 구현, 추상 메서드(앞에 abstract 생략되어있음)
// 추상메서드 안에서 default 메서드를 이용해서 메서드 정의가 가능하다.
// 디폴트 메서드 선택 구현
// default public void reaDB() {}
} //인터페이스 종료
class OuterClass {
private String myName;
OuterClass(String name) {
myName = name;
}
public Readable createLocalClassInst(int instID) {
// 로컬 클래스
// 디폴트 리드DB가 필요하면 오버라이드 하면된다.
class LocalClass implements Readable {
public void read() {
System.out.println("Outer inst name: " + myName);
System.out.println("Local inst ID: " + instID);
}
}
return new LocalClass(); //로컬 인스턴스 자식(localclass)을 조상(readable)에 반환
}
}
class LocalParamClassTest {
public static void main(String[] args) {
OuterClass out = new OuterClass("My Outer Class");
Readable localInst1 = out.createLocalClassInst(111);
localInst1.read();
Readable localInst2 = out.createLocalClassInst(222);
localInst2.read();
}
}
ㄴ 익명 클래스로 변환시킬 수 있다.
public Readable createLocalClassInst(int instID) {
// 로컬(익명) 클래스
// 디폴트 리드DB가 필요하면 오버라이드 하면된다.
return new Readable() {
public void read() {
System.out.println("Outer inst name: " + myName);
System.out.println("Local inst ID: " + instID);
}
};
}
ㄴ new 조상인터페이스() { 구현 };
ㄴ 클래스의 역할도 하게된다. 클래스를 정의할 필요가 없다.
ㄴ ; 는 내용과 리턴해야하기 때문이다.
- Lamda의 이해
- 람다식은 최대한 코드를 줄여쓰는 것이 좋다.
- 인터페이스 참조변수 = new 구현하는 클래스 정의 대신 람다식을 정의한다.
- 람다식 형식: (매개변수명) -> { 명령어; }
람다 예제1)
package ex1;
interface Printable {
void print(String s);
}
public class LamdaTest {
public static void main(String[] args) {
Printable prn = new Printable() {
public void print(String s) {
System.out.println(s);
}
};
prn.print("What is Lamda?");
}
}
람다 예제1_1)
package ex1;
interface Printable {
void print(String s);
}
public class LamdaTest {
public static void main(String[] args) {
Printable prn;
prn = (String s) -> {System.out.println(s);}; //람다 1단계
prn = (String s) -> System.out.println(s); //람다 2단계
prn = (s) -> System.out.println(s); //람다 3단계
prn = s -> System.out.println(s); //람다 4단계 ***
prn.print("What is Lamda?");
}
}
람다 예제2)
package ex1;
interface Printable {
void print(String s); // 1개만 구현
}
public class LamdaTest {
public static void main(String[] args) {
showString((s) -> {
System.out.println(s);
}, "Hello Lamda");
}
public static void showString(Printable p, String str) {
p.print(str);
}
}
ㄴ 이제 구현할 인터페이스를 자바에서 준다.
ㄴ 이를 함수형 인터페이스라고 하고 아래를 보도록 하자.
- 람다 표현식
- 람다와 함수형 인터페이스
※ 참고
List<? super person>
ㄴ person과 조상만 상속
List<? extends person>
ㄴ person과 후손만 상속
- 매개변수가 둘 인 람다식
- 매개변수가 있고 반환하는 람다식
- 리턴문(return)이 있을 때 중괄호 생략 불가능
예제1)
package ex1;
interface Calculate {
int cal(int a, int b); // 값을 반환하는 추상 메소드
}
class TwoParamAndReturn {
public static void main(String[] args) {
Calculate c;
c = (a, b) -> {
return a + b;
}; // 중괄호 생략 불가
System.out.println(c.cal(4, 3));
// 연산 결과가 남으면, return을 명시하지 않아도 됨
// 반환값이 있는 경우 굳이 return을 쓰지 않아도 된다.
c = (a, b) -> a + b;
System.out.println(c.cal(4, 3));
}
}
예제5)
package ex1;
interface HowLong {
int len(String s); // 값을 반환하는 메소드
}
class OneParamAndReturn {
public static void main(String[] args) {
HowLong hl = s -> s.length();
System.out.println(hl.len("I am so happy"));
}
}
- 매개변수 없는 메서드
- 함수형 인터페이스 : 추상 메서드가 따가 하나만 존재하는 인터페이스
package ex1;
//추상 메소드가 하나이니까, 함수형 인터페이스에 해당
@FunctionalInterface
interface Calculate2 {
int cal(int a, int b);
default int add(int a, int b) {
return a + b;
}
static int sub(int a, int b) {
return a - b;
}
}
- 람다식과 제네릭
- 제네릭은 기본 타입이 참조형이다.
문1) 아래 코드에서 주석에 명시된 연산의 결과를 출력하기 위한 calAndShow 메소드의 호출문을 람다식 기반으로 작성해보자.
interface Calculate<T> {
T cal(T a, T b);
}
class CalculatorDemo {
public static <T> void calAndShow(Calculate<T> op, T n1, T n2){
T r = op.cal(n1, n2);
System.out.println(r);
}
public static void main(String[] args) {
// 3 + 4
// 2.5 + 7.1
// 4 -2
// 4.9 - 3.2
}
}
풀이)
package ex1;
interface Calculate4<T> {
T cal(T a, T b);
}
class CalculatorDemo {
public static <T> void calAndShow(Calculate4<T> op, T n1, T n2) {
T r = op.cal(n1, n2);
System.out.println(r);
}
public static void main(String[] args) {
// 3 + 4
calAndShow((a,b) -> a+b, 3, 4);
// 2.5 + 7.1
calAndShow((a,b) -> a+b, 2.5, 7.1);
// 4 - 2
calAndShow((a,b) -> a-b, 4, 2);
// 4.9 - 3.2
calAndShow((a,b) -> a-b, 4.9, 3.2);
}
}
문2) <예제1> SLenComparator.java를 람다식 기반으로 수정해 보자. 수정 결과에서는 클래스 SLenComp의 정의가 지워져야 한다.
package ex1;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class SLenComp implements Comparator<String> {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
}
class SLenComparator {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Robot");
list.add("Lambda");
list.add("Box");
// 두 번째 인자로 인스턴스를 전달하고 있지만 내용은 메소드(기능)를 전달함.
Collections.sort(list, new SLenComp());
for (String s : list)
System.out.println(s);
}
}
- 정의되어 있는 함수형 인터페이스
- 대표적인 함수형 인터페이스
Predicate<T> | boolean test(T t) 전달 인자를 근거로 참 또는 거짓을 반환 |
Supplier<T> // 제공자 |
T get() 메서드 호출 시 무엇인가를 제공함 |
Consumer<T> //소비자 |
void accept(T t) 인자를 받아 들이기만 함 |
Function<T, R> | R apply(T t) 입출력 출력이 있음(수학적으로는 함수) |
- Predicate<T> 함수형 인터페이스 boolean test(T t)
package ex2;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateDemo {
public static int sum(Predicate<Integer> p, List<Integer> lst) {
int s = 0;
for (int n : lst) {
if (p.test(n))
s += n;
}
return s;
}
public static void main(String[] args) {
//asList ; 값을 배열로 반환
//Arrays. ; 배열
List<Integer> list = Arrays.asList(1, 5, 7, 9, 11, 12);
int s;
s = sum(n -> n % 2 == 0, list);
System.out.println("짝수합: " + s);
s = sum(n -> n % 2 != 0, list);
System.out.println("홀수합: " + s);
}
}
문제3) 아래의 코드에서 주석으로 표시된 내용의 출력을 보이도록 show메소드의 몸체를 채워 보자.
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateShow{
public static <T> void show(Predicate<T> p, List<T> lst) {
//채워 넣을 부분
}
public static void main(String[] args) {
List<Integer> lst1 = Arrays.asList(1, 3, 8, 10, 11);
show(n -> n%2!=0, lst1); //홀수만 출력
List<Double> lst2 = Arrays.asList(-1.2, 3.5, -2.4, 9.5);
show(n -> n > 0.0, lst2); //0.0 보다 큰 수 출력
}
풀이)
package ex2;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateShow {
public static <T> void show(Predicate<T> p, List<T> lst) {
for (T n : lst) {
if (p.test(n))
System.out.println(n);
}
}
public static void main(String[] args) {
List<Integer> lst1 = Arrays.asList(1, 3, 8, 10, 11);
show(n -> n % 2 != 0, lst1); // 홀수만 출력
List<Double> lst2 = Arrays.asList(-1.2, 3.5, -2.4, 9.5);
show(n -> n > 0.0, lst2); // 0.0 보다 큰 수 출력
}
}
1
3
11
3.5
9.5
- Predicate<T>를 구체화하고 다양화한 인터페이스들
- java의 속도 개선 차원에서 자료형을 구체화한 것이다.
- 문제4>아래 코드가 정상적으로 동작하도록 ‘완성되지 않은 문장’을 완성해 보자. 어떠 한 내용을 담아야 할지는 주석의 내용을 참조하여 판단하자.
package ex2;
import java.util.function.BiPredicate;
public class BiPredicateDemo {
public static void main(String[] args) {
BiPredicate<String, Integer> conv = (str, n) -> str.length() > n;
//test 호출 결과 문자열 “Robot"의 길이가 3을 넘으면 true 반환
if (conv.test("Robot", 3))
System.out.println("문자열 길이 3 초과");
else
System.out.println("문자열 길이 3 이하");
//test 호출 결과 문자열 “Box"의 길이가 5를 넘으면 true 반환
if (conv.test("Box", 5))
System.out.println("문자열 길이 5 초과");
else
System.out.println("문자열 길이 5 이하");
}
}
- Supplier함수형 인터페이스 T get()
package ex3;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Supplier;
public class SupplierDemo {
public static List<Integer> makeIntList(Supplier<Integer> s, int n) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < n; i++)
list.add(s.get()); // 난수를생성해담는다.
return list;
}
public static void main(String[] args) {
Supplier<Integer> spr = () -> {
Random rand = new Random();
return rand.nextInt(50);
};
List<Integer> list = makeIntList(spr, 5);
System.out.println(list);
list = makeIntList(spr, 10);
System.out.println(list);
}
}
- Supplier<T>를 구체화하고 다양화 한 인터페이스들
- Consumer<T>함수형 인터페이스 ☞ void accept(T t)
- Consumer<T>를 구체화하고 다양화 한 인터페이스들
문제5) 다음은 제네릭을 공부할 때 정의했던 클래스이다. 인터페이스 BiConsumer 를 기반으로 위 클래스의 인스턴스에 int형, double형 데이터를 저장하는 기능의 람 다식을 각각 작성하고, 이를 확인하기 위한 예제를 작성해보자.
package ex3;
import java.util.function.BiConsumer;
class Box<T> {
private T ob;
public void set(T o) {
ob = o;
}
public T get() {
return ob;
}
}
public class BiConsumerTest {
public static void main(String[] args, int ArrayList) {
BiConsumer<Box<Integer>, Integer> b1 = (b,i) -> b.set(i); //set활용 accept 구현
BiConsumer<Box<Double>, Double> b2 = (b,i) -> b.set(i);
Box<Integer> bi = new Box<>();
Box<Double> bd = new Box<>();
b1.accept(bi, 10);
b2.accept(bd, 5.59);
System.out.println(bi.get());
System.out.println(bd.get());
}
}
ㄴ 다시보기
- Function<T(입력), R(출력)>함수형 인터페이스 -> R apply(T t);
package ex3;
import java.util.Scanner;
import java.util.function.Function;
public class FunctionDemo2 {
public static void main(String[] args) {
Function<Double, Double> cti = d -> d * 0.393701;
Function<Double, Double> itc = d -> d * 2.54;
Scanner sc = new Scanner(System.in);
System.out.print("정수 입력 : ");
int n = sc.nextInt();
System.out.println(n+"cm= " + cti.apply((double) n) + " inch"); // cm를 inch로
System.out.println(n+"inch= " + itc.apply((double) n) + " cm"); // inch를 cm로
}
}
- Function<T(입력), R(출력)>을 구체화하고 다양화 한 인터페이스들
- removeIf 메소드 ; 조건 삭제
package ex3;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class RemoveIfDemo {
public static void main(String[] args) {
List<Integer> ls1 = Arrays.asList(1, -2, 3, -4, 5);
ls1 = new ArrayList<>(ls1);
List<Double> ls2 = Arrays.asList(-1.1, 2.2, 3.3, -4.4, 5.5);
ls2 = new ArrayList<>(ls2);
List<Integer> ls3 = Arrays.asList(1, -2, 3, -4, 5);
ls3 = new ArrayList<>(ls3);
List<Double> ls4 = Arrays.asList(-1.1, 2.2, 3.3, -4.4, 5.5);
ls4 = new ArrayList<>(ls4);
Predicate<Number> p = n -> n.doubleValue() < 0.0; // 삭제의 조건
Predicate<Number> p1 = n -> n.doubleValue() > 0.0;
ls1.removeIf(p); // List<Integer> 인스턴스에 전달
ls2.removeIf(p); // List<Double> 인스턴스에 전달
ls3.removeIf(p1); // List<Integer> 인스턴스에 전달
ls4.removeIf(p1); // List<Double> 인스턴스에 전달
System.out.println(ls1);
System.out.println(ls2);
System.out.println(ls3);
System.out.println(ls4);
}
}
- 메서드 참조
- 기본적으로 람다식보다 조금 더 코드를 단순하게 할 수 있다는 장점이 있다.
- 일부 람다식을 메소드 참조로 대신하게 할 수 있다.
package ex4;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
public class ArrangeList1 {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
// reverse 메소드 호출 중심의 람다식 기반
// Consumer<List<Integer>> c = l -> Collections.reverse(l); //accept 구현
// static 메소드 참조 기반
Consumer<List<Integer>> c = Collections::reverse;
c.accept(ls); // ls는 그대로 reverse에 전달되고, 순서 뒤집기 진행.
System.out.println(ls); // 9 7 5 3 1 출력
}
}
1.2 인스턴스 메소드 참조1 : effectively final(사실상 상수)
package ex4;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
class JustSort {
public void sort(List<?> lst) { // 인스턴스메소드
Collections.reverse(lst);
}
}
public class ArrangeList3 {
public static void main(String[] args) {
List<Integer> ls = Arrays.asList(1, 3, 5, 7, 9);
ls = new ArrayList<>(ls);
JustSort js = new JustSort(); // js는effectively final
// js=new JustSort(); 에러
Consumer<List<Integer>> c = e -> js.sort(e); // 람다식기반
Consumer<List<Integer>> c = js::sort; // 인스턴스 메소드 참조 기반
c.accept(ls);
System.out.println(ls);
// js=null; 에러
}
}
package ex4;
import java.util.Arrays;
import java.util.List;
public class ForEachDemo {
public static void main(String[] args) {
List<String> ls = Arrays.asList("Box", "Robot");
ls.forEach(s -> System.out.println(s)); // 람다식 기반
ls.forEach(System.out::println); // 메소드 참조 기반
}
}
- Collection 이 Iterable을 상속하기 때문에 대부분 컬렉션 클래스는 forEach를 사용 가능함.
- Iterable의 디폴트 메소드
default void forEach(Consumer<? super T> action) {
for (T t : this) // this는 이 메소드가 속한 컬렉션 인스턴스를 의미함
action.accept(t); // 모든 저장된 데이터들에 대해 이 문장 반복
}
1.3 인스턴스 메소드 참조2 : 인스턴스 없이 인스턴스 메소드 참조
package ex4;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class StrIgnoreCaseComp {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("robot");
list.add("Lambda");
list.add("box");
// Collections.sort(list, (s1, s2) -> s1.compareToIgnoreCase(s2));
Collections.sort(list, String::compareToIgnoreCase);
System.out.println(list);
}
}
'ICIA 수업일지' 카테고리의 다른 글
2021.07.16 수업일지 (0) | 2021.07.17 |
---|---|
2021.07.15 수업일지 (0) | 2021.07.17 |
2021.07.13 수업일지 (0) | 2021.07.17 |
2021.07.12 수업일지 (0) | 2021.07.17 |
2021.07.09 수업일지 (0) | 2021.07.12 |