본문 바로가기
IT/JAVA

[17일차] JAVA Generic / lambda

by GWLEE 2022. 7. 12.

<< 2022-07-12 THU >>

 

제네릭
데이터의 타입(data type)을 일반화한다(generalize)는 것을 의미

제네릭은 클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법입니다.

이렇게 컴파일 시에 미리 타입 검사(type check)를 수행하면 다음과 같은 장점을 가집니다.


객체를 생성하는 시점에서 String , Person , Integer용으로 정해주는 



 

 

문자열이 아닌 정수나 다른게 들어갈 수 있는데 다 받아줄 수 있다. 그게 또 문제

프로그래머가 전부 책임을 져야하는데 성능저하.. 타입체킹불가능 등 여러가지 문제 발생

 

Person.java

 

Box.java

 

BoxPerson.java

 

BoxMain.java

 

Console

 


 

BoxGeneric.java

 

GenericMain.java

 

 

Console

 


Pair.java

 

Util.java

main static <K, V> boolean compare 왜 static? main에서 써야하니깐 지정

 

PairMain.java

Console

 


PairMain.java

Console

 


  <제한된 타입 파라미터>
 - T extends 상위타입 (인터페이스도 가능, 이 경우에도 extends 키워드 사용)
 - 타입 파라미터에 지정되는 구체적인 타입은 상위 타입이거나 상위 타입의 하위 또는 구현클래스만 가능
 - 메소드내에서 타입 파리미터 변수를 가지고 사용가능한 것은 상위 타입의 필드와 메소드로 제한된다.

 

Util2.java

Util2Main.java

Console

-1, 0, 1 로 찍힘 

 


 

<와일드카드 타입>

<?> : Unbounded Wildcards (제한 없음)
    모든 클래스나 인터페이스가 올 수 있음

<? extends T> Upper Bounded Wildcards (상위 클래스 제한)
    T 타입이나 T의 하위타입만 올 수 있음

<? super T> Lower Bounded Wildcards (하위 클래스 제한)
    T 타입이나 T의 상위타입만 올 수 있음

 

 

Person을 상속받은 worker 

 

Course.java

 

Person.java

 

Worker.java

 

Student.java

 

GraduateStudent.java

 

Course.Main

package com.gyuone.generic3;

import java.util.Arrays;

public class CourseMain {

	public static void registerCourse(Course<?> course) {
		System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));
	}

	public static void registerCourseStudent(Course<? extends Student> course) {
		System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));// 학생만 등록
	}

	public static void registerCourseWorker(Course<? super Worker> course) {
		System.out.println(course.getName() + "수강생 : " + Arrays.toString(course.getStudents()));

	}

	public static void main(String[] args) {
		Course<Person> personCourse = new Course<Person>("일반인과정", 5);
		personCourse.add(new Person("일반인"));
		personCourse.add(new Worker("직장인"));
		personCourse.add(new Student("학생"));
		personCourse.add(new GraduateStudent("대학원생"));

		Course<Worker> workerCourse = new Course<>("직장인 과정", 5);
		workerCourse.add(new Worker("직장인"));

		Course<Student> studentCourse = new Course<>("학생과정", 5);
		studentCourse.add(new Student("학생"));
		studentCourse.add(new GraduateStudent("대학원생"));

		Course<GraduateStudent> graduateCourse = new Course<>("대학원과정", 5);
		graduateCourse.add(new GraduateStudent("대학원생"));

		registerCourse(personCourse);
		registerCourse(workerCourse);
		registerCourse(studentCourse);
		registerCourse(graduateCourse);
		System.out.println("=====================================================================");

		registerCourseStudent(studentCourse);
		registerCourseStudent(graduateCourse);
//		registerCourseStudent(personCourse); 제한 걸어서 안됨
//		registerCourseStudent(workerCourse);

		registerCourseWorker(workerCourse);
		registerCourseWorker(personCourse); // 부모타입 
//		registerCourseWorker(studentCourse);
//		registerCourseWorker(graduateCourse);
	}
}

 


추상메서드 나를 상속받은 자식 클래스가 define

인터페이스를 구현 하는 구현 클래스

 

한 번 쓰고 말건데 인터페이스가 필요.. 이 문제를 해결하기 위해서는 

 

<람다식>

 - 람다식은 매개변수를 가지는 코드블럭
 - 객체지향 프로그램에 함수적 프로그래밍을 도입
 - 이벤트 지향 프로그램에 적합
 - 자바 코드가 간결해짐
 - JVM이 런타임 시에 매개변수를 가지는 코드블럭을 익명객체로 전환

파이썬에서 람다식은 익명함수를 구현하기 위해서 쓴다.

자바에서 람다식은 인터페이스에 익명구현객체를 만드는 용도로 쓴다.

 

람다식을 쓰는 방법

(타입 매개변수, ...) -> { 실행문; ....}

 (int a) -> { System.out.println(a) }
 (a) -> { System.out.println(a) } // 람다에서는 타입 생략가능
  a -> System.out.println(a) // 중괄호도 생략가능 

 매개변수가 없을 경우
 () -> { 실행문; ....} // 매개변수 없을 경우 괄호 필요

 리턴문이 있을 경우
 (x, y) -> { .... return x + y; }

 리턴문만 있을 경우
 (x, y) -> x + y

 인터페이스(target type) 변수 = 람다식;
 
 람다식의 타겟이 되는 인터페이스는 추상메서드를 딸랑 1개만 갖고있다.
 어떤 람다식이 타겟타입으로 설정할 필요가 없다.

 

추상메서드의 내용 부분을 채워넣는.. 형태

 

하지만 제약사항이 있다.

<함수적 인터페이스> = functional interface

 - 모든 인터페이스를 람다식의 타겟타입으로 사용할 수 없음
 - 람다식은 하나의 메소드를 정의
 - 하나의 추상메소드를 가지는 인터페이스만 람다식의 타겟 타입이 됨 (함수적 인터페이스)
 - 함수적 인터페이스 작성 시 실수를 방지하기 위해 컴파일러가 체킹해 주기를 원하는 경우
   @FunctioalInterface 어노테이션을 사용

실수로 두개 만들면 컴파일에서 막아준다. 인터페이스는 추상메서드를 하나만 갖고있기 때문.


 

 

 

 

인터페이스 Storage

 

 

 


인터페이스 FuncInter.java

 

 

method 호출하는 3가지 방법

 

클래스 / 익명/ 람다함수 사용 

셋 중에서는 람다 사용이 편하다.

 

FuncMain01

 

 

 

 

 


 

FunMain02.java

 

람다식을 다양하게 표현하는 방법 slack 참고 **

 

 

 

 


 

 

 

 

 


<표준 API 함수적 인터페이스>
 - 자바에서 제공되는 표준API에서 한 개의 추상메소드를 가지는 인터페이스는 모두 람다식을
   이용해서 익명구현객체로 표현 가능

 

 

 - 자바8 부터 java.util.function 패키지에 자주 사용되는 함수적 인터페이스를 표준 API로 제공
   제공되는 인터페이스는 아래와 같은 5가지
   
 - Consumer: 매개값은 있고 리턴값이 없음
 - Supplier: 매개값이 없고 리턴값이 있음 // comsumer에 반대
 - Function: 매개값, 리턴값 모두 있음(주로 매개값은 리턴값으로 매핑)
 - Operator: 매개값, 리턴값 모두 있음(주로 매개값을 연산하고 결과를 리턴)
 - Predicate: 매개값은 있고 리턴 타입은 boolean(매개값을 조사해서 true/false 리턴)

 

람다식을위한 function interface

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/package-summary.html

 

java.util.function (Java SE 11 & JDK 11 )

Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and retu

docs.oracle.com

 

 

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/IntSupplier.html

 

IntSupplier (Java SE 11 & JDK 11 )

 

docs.oracle.com

 

 

 

 

 


 

 

 

 

 

package com.gyuone.lambda2;

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.ToIntFunction;

public class FunctionMain {
	// Student라는 객체를 여러 개 갖고 있음.
	private static List<Student> list = Arrays.asList(new Student("홍길동", 95, 80), new Student("이순신", 90, 85) // 메인메소드에서
																												// 사용할거니깐
																												// static
	);

	public static void printString(Function<Student, String> function) { // string 결과값 익명 구현객체를 어디서 만들어서 넘겨만 주면
		// 학생 두명 리스트를 뽑아내고 
		for (Student student : list) {
			System.out.print(function.apply(student) + " "); // apply 메서드에 학생을 집어넣겠다. apply 돌려주는
			// 결과 값은 문자로 나온다.
		}
		System.out.println();
		System.out.println("========================================================");
	}

	public static void printInt(ToIntFunction<Student> function) { //
		for (Student student : list) {
			System.out.println(function.applyAsInt(student) + " ");
		}
		System.out.println();
		System.out.println("========================================================");
	}

	public static void main(String[] args) {
		System.out.print("[학생이름]");
		printString(t -> t.getName()); // t.getName -> 학생이 들어감 . apply 메서드 내용을 정의 람다식
//		Function<Student, String> f1 = t -> t.getName();
//		printString(f1);
		
		System.out.println("[영어점수]");
		printInt(t -> t.getEnglishScore());
		//  넘겨줄 때 reference 값만 줬는데 처리하는 코드형태로 처리..
		// 처리하는 루틴을 지정해서 넘겨준다. 내부적으로는 reference 값만 넘겨주는데 구조상 처리하는 코드로 넘긴다.

		System.out.println("[수학점수]");
		printInt(t -> t.getMathScore());
	}
}

 

 

 


 

94 94 비교 숫자 두개를 비교하는 메소드 

 

 

 


https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/function/Predicate.html

 

Predicate (Java SE 11 & JDK 11 )

 

docs.oracle.com

 

Predicate :

  • argument를 받아 boolean 값을 반환하는 함수형 인터페이스

 

Console

'IT > JAVA' 카테고리의 다른 글

[18일차] JAVA 컬렉션 프레임워크  (0) 2022.07.13
[17일차] JAVA Programmers 개념정리  (0) 2022.07.12
[16일차] JAVA 예외처리. 스레드  (0) 2022.07.11
[15일차] JAVA Programmers 개념정리  (0) 2022.07.08
[15일차] JAVA  (0) 2022.07.08

댓글