본문 바로가기
ICIA 수업일지

2021.07.14 수업일지

by 주성씨 2021. 7. 17.

- 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