비트교육센터/자바

[비트교육센터] 자바 17일차 제한된 파라미터, 와일드카드, 람다

달의요정루나 2023. 6. 23. 19:40

1. 제한된 파라미터

- 제한된 파라미터

1) 경우에 따라 타입 파라미터를 대체하는 구체적인 타입을 제한할 필요가 있다.

2) 예를 들어 숫자를 연산하는 제네릭 메소드는 대체타입으로 Number 또는 자식 클래스(Byte, Short, Integer, Long, Double)로 제한할 필요가 있다.

3) 이처럼 모든 타입으로 대체할 수 없고, 특정 타입과 자식 또는 구현 관계에 있는 타입만 대체할 수 있는 타입 파라미터를 제한된 파라미터라고 한다.

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

1) 제한된 파라미터

[1] Util. java

package com.julian5383.train21;

public class Util {
	public static <T extends Number> int compare(T t1, T t2) {
		//<T extends Number>은 제너릭 메소드 명시
        //타입 파라미터 T를 대체할 타입을 Number로 제한
		
		double v1 = t1.doubleValue();
		double v2 = t2.doubleValue();
		//Number 타입의 doubleValue() 메소드 호출
		
		return Double.compare(v1, v2);
		//2개가 같은면 0 , v1이 작으면 음수, v2이 작으면 양수 (v1-v2)
	}

}

- Number(java.lang.Number): 추상 클래스로 기본 유형인 byte, double, float, int, long, 및 short로 변환할 수 있는 숫자 값을 나타내는 플랫폼 클래스의 수퍼클리스이다.

[2] UtilMain. java

package com.julian5383.train21;

public class UtilMain {

	public static void main(String[] args) {
		int result01 = Util.compare(10, 20);
		System.out.println(result01);
		
		int result02 = Util.compare(4.5, 2.0);
		//4.5f, 2.0일시 double쪽으로 자동 형변환
		System.out.println(result02);
		
		//int result03 = Util.compare("Hello", "Hi");
		//System.out.println(result03);
		//String객체는 불가
	}

}

--> 결과

-1
1

2. 와일드카드 타입 파라미터

- 제네릭 타입을 매개값이나 리턴 타입으로 사용시 파라미터로 ?(와일드카드)를 사용할 수 있다.

- ?는 범위에 있는 모든 타입으로 대체할 수 있다는 표시이다.

 

<와일드카드 타입>
<?> : Unbounded Wildcards (제한 없음)
   모든 클래스나 인터페이스가 올 수 있음
<? extends T> Upper Bounded Wildcards (상위 클래스 제한)
   T 타입이나 T의 하위타입만 올 수 있음
<? super T> Lower Bounded Wildcards (하위 클래스 제한)
   T 타입이나 T의 상위타입만 올 수 있음

1) 와일드카드 파라미터

[1] Course.java

package com.julian5383.train22;

public class Course<T> {
	private String name;
	private T[] students;
	
	public Course(String name, int capacity) {
		this.name = name;
		this.students = (T[])new Object[capacity];
		//칸이 5개인 배열객체를 입력
	}

	public String getName() {
		return name;
	}

	public T[] getStudents() {
		return students;
	}
	
	public void add(T t) {
		for (int i = 0; i < students.length; i++) {
			if(students[i] == null) {
				students[i]=t;
				break;
			}
		}
		//배열 5칸에서 순서대로 넣어주는 것을 구현
	}
}

[2] Person.java

package com.julian5383.train22;

public class Person {
	private String name;

	public Person(String name) {
		this.name = name;
	}

}

[3] Worker.java

package com.julian5383.train22;

public class Worker extends Person{

	public Worker(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}
	
}

[4] Student.java

ackage com.julian5383.train22;

public class Student extends Person{

	public Student(String name) {
		super(name);
		//상속과정에서는 자식 클래스 생성자에서는 첫줄에 부모클래스 생성자 명시
		// TODO Auto-generated constructor stub
	}
	
}

[5] GraduateStudent.java

package com.julian5383.train22;

public class GraduateStudent extends Student{

	public GraduateStudent(String name) {
		super(name);
		// TODO Auto-generated constructor stub
	}
	
}

[6] CourseMain.java

package com.julian5383.train22;

import java.util.Arrays;

public class CourseMain {
	public static void registerCourse(Course<?> course) {
		System.out.println(course.getName() + " 수강생");
		System.out.println(Arrays.toString(course.getStudents()));
		System.out.println();
	}
	//모든 사람이 수강가능
	
	public static void registerCourseStudent(Course<? extends Student> course) {
		System.out.println(course.getName() + " 수강생");
		System.out.println(Arrays.toString(course.getStudents()));
		System.out.println();
	}
	//학생들만 수강가능
	
	public static void registerCourseWorker(Course<? super Worker> course) {
		System.out.println(course.getName() + " 수강생");
		System.out.println(Arrays.toString(course.getStudents()));
		System.out.println();
	}
	//직장인만 수강가능
	
	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<Worker>("직장인과정", 5);
		workerCourse.add(new Worker("직장인"));
		//직장인만 신청가능
		
		Course<Student> studentCourse = new Course<Student>("학생과정", 5);
		studentCourse.add(new Student("학생"));
		studentCourse.add(new GraduateStudent("대학원생"));
		//학생만 신청가능
		
		Course<GraduateStudent> graduateStudentCourse = new Course<GraduateStudent>("대학원과정", 5);
		graduateStudentCourse.add(new GraduateStudent("대학원생"));
		//대학원생만 신청가능
		
		registerCourse(personCourse);
		registerCourse(workerCourse);
		registerCourse(studentCourse);
		registerCourse(graduateStudentCourse);
		System.out.println();
		
		registerCourseStudent(studentCourse);
		registerCourseStudent(graduateStudentCourse);
//		registerCourseStudent(personCourse);
//		registerCourseStudent(workerCourse);
		System.out.println();
		
		registerCourseWorker(personCourse);
		registerCourseWorker(workerCourse);
//		registerCourseWorker(studentCourse);
//		registerCourseWorker(graduateStudentCourse);
	}
}
Course<?> : Person, Worker, Student, GraduateStudent 가 대상
Course<? extends Student> : Student, GraduateStudent 가 대상
Course<? super Worker> : Worker, Person 만 대상

--> 결과

일반인과정 수강생
[com.julian5383.train22.Person@28a418fc, com.julian5383.train22.Worker@5305068a, com.julian5383.train22.Student@1f32e575, com.julian5383.train22.GraduateStudent@279f2327, null]

직장인과정 수강생
[com.julian5383.train22.Worker@2ff4acd0, null, null, null, null]

학생과정 수강생
[com.julian5383.train22.Student@54bedef2, com.julian5383.train22.GraduateStudent@5caf905d, null, null, null]

대학원과정 수강생
[com.julian5383.train22.GraduateStudent@27716f4, null, null, null, null]


학생과정 수강생
[com.julian5383.train22.Student@54bedef2, com.julian5383.train22.GraduateStudent@5caf905d, null, null, null]

대학원과정 수강생
[com.julian5383.train22.GraduateStudent@27716f4, null, null, null, null]


일반인과정 수강생
[com.julian5383.train22.Person@28a418fc, com.julian5383.train22.Worker@5305068a, com.julian5383.train22.Student@1f32e575, com.julian5383.train22.GraduateStudent@279f2327, null]

직장인과정 수강생
[com.julian5383.train22.Worker@2ff4acd0, null, null, null, null]

3. 람다식

- 람다식은 데이터 처리부에 제공되는 함수 역할을 하는 매개변수를 가진 중괄호 블록이다.

- 데이터 처리부는 람다식을 받아 매개변수에 데이터를 대입하고 중괄호를 실행시켜 처리한다.

- 언어를 함수식으로 구현하는 것을 고려하면서 만들어졌다.

람다식: (매개변수, ...) -> { 처리 내용 }

- 자바 람다: Function Interface의 익명구현객체를 만들 때 사용하기 위한 용도이다.

- 파이썬 람다: 익명 함수를 만들기 위해 도입한 표기법이다.

1) 람다식과 익명구현객체

[1] AddInterface.java

package com.julian5383.train23;

public interface AddInterface {
	public int add(int a, int b);
}

[2] AddMain.java

package com.julian5383.train23;

public class AddMain {
	public static void main(String[] args) {
		AddInterface a = new AddInterface() {
			
			@Override
			public int add(int a, int b) {
				// TODO Auto-generated method stub
				return a+b;
			}
		};
		System.out.println(a.add(3, 4));
		//인터페이스 익명구현객체
		
		AddInterface result = (c,d) -> c+d;
		System.out.println(result.add(10, 50));
		//람다표현식으로 구현
	}

}

- 람다 표현식을 보면 추상메소드 add()는 2개의 매개변수를 가져 (c,d)로 표현했고, -> 뒤에 add()의 실행블록이 온다.

- 추상메소드 하나가 있는 인터페이스가 람다식의 타겟타입이다.

- Function Interface: 함수형 인터페이스로 추상메소드가 오직 하나이다.

--> 결과

7
60

2) 파리미터와 리턴값이 없는 람다식

[1] NoParamNoReturn.java

package com.julian5383.train23;

public interface NoParamNoReturn {
	public void method();
}

[2] LambdaMain.java

package com.julian5383.train23;

public class LambdaMain {
	public static void main(String[] args) {
		NoParamNoReturn no = () -> {
			System.out.println("Hello");
		};
		
		//NoParamNoReturn no = () -> System.out.println("Hello");
		//body에 1줄밖에 없으면 중괄호 생략가능
		
		no.method();
		
		no = ()->{
			System.out.println("============");
		};
		no.method();

	}
}

==> 결과

Hello
============

 

3) 리턴값이 없는 람다식

[1] NoReturn.java

package com.julian5383.train23;

public interface NoReturn {
	public void method(int x);
}

 

[2] LambdaMain.java

package com.julian5383.train23;

public class LambdaMain {
	public static void main(String[] args) {
		NoReturn no2 = (a)-> System.out.println(a*2);
		//NoReturn no2 = a-> System.out.println(a*2);
		//입력 파라미터가 1개일때 소괄호 생략가능
		no2.method(10);
		
	}
}

--> 결과

20

4) 파리미터와 리턴값이 있는 람다식

[1] Dolda.java

package com.julian5383.train23;

public interface Dolda {
	public int method(int x, int y);
}

[2] LambdaMain.java

package com.julian5383.train23;

public class LambdaMain {
	public static void main(String[] args) {	
		
		Dolda dolda = (a,b)->{
        		return a*b;
        	};
		//문장이 return문이라서 중괄호 생략 못함
		//Dolda dolda = (a,b)-> a*b; //return, 중괄호를 지우면 가능
		System.out.println(dolda.method(2, 5));
		
	}
}

--> 결과

10

5) Thread 람다

[1] FlagGame.java(익명객체)

package com.julian5383.train24;

public class FlagGame {
	public static void main(String[] args) {		
		Thread thread = new Thread(new Runnable() {	//run메소드 밖에 없음	
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for (int i = 0; i < 10; i++) {
					System.out.println("청기");
					try {
						Thread.sleep(500);
					} catch (Exception e) {
						// TODO: handle exception
					}
				}
			}
		});//익명객체 선언

		thread.start();
		
		for (int i = 0; i < 10; i++) {
			System.out.println("백기");
			try {
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}

}

[2] FlagGame.java(람다식)

package com.julian5383.train24;

public class FlagGame {
	public static void main(String[] args) {				
		Thread thread = new Thread(()->{
			for (int i = 0; i < 10; i++) {
				System.out.println("청기");
				try {
					Thread.sleep(500);
				} catch (Exception e) {
					// TODO: handle exception
				}
			}
		});//람다식
		thread.start();
		
		for (int i = 0; i < 10; i++) {
			System.out.println("백기");
			try {
				Thread.sleep(500);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}

}

- Runnable 객체를 생략하고 쓴다.

- 타겟타입: 추상메소드가 하나 있는 인터페이스

- 스레드의 타입변수가 runnable로 들어가면 Thread가 Thread(Runnable target)구조로 인식해서 Runnable객체를 지울수 있다.

https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Thread.html

 

Thread (Java SE 17 & JDK 17)

All Implemented Interfaces: Runnable Direct Known Subclasses: ForkJoinWorkerThread A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently. Every thread has a

docs.oracle.com

--> 결과

백기
청기
청기
백기
청기
백기
청기
백기
청기
백기
청기
백기
청기
백기
청기
백기
청기
백기
청기
백기

6) 함수적 인터페이스 5가지

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

[1] ConsumerTest.java

package com.julian5383.ex01;

import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.DoubleConsumer;
import java.util.function.ObjIntConsumer;

public class ConsumerTest {
	public static void main(String[] args) {
		Consumer<String> consumer=t->System.out.println(t+" Class");
		consumer.accept("Java");
		
		BiConsumer<String, String> biConsumer=(t,u)->System.out.println(t+u);
		biConsumer.accept("Java", "Class");
		
		DoubleConsumer doubleConsumer = d -> System.out.println(d + "를 10으로 나눈 나머지는 " + d % 10 + "입니다.");
		doubleConsumer.accept(14.0);
		
		ObjIntConsumer<String> objIntConsumer = (t, i) -> System.out.println(t + i);
		objIntConsumer.accept("Java", 8);
		

	}

}

- Consumer: 단일 입력 인자를 받아들이고(accept 메소드) 결과값을 리턴하지 않는다.(리턴값이 없음)

- BiConsumer: 단일 입력 인자만 받아들이는 Consumer와 달리 BiConsumer는 2개의 인자를 받아들이고 결과값을 리턴하지 않는다.

- DoubleConsumer: 실수형 단일 입력 인자를 받아들이고 결과값을 리턴하지 않는다.

- ObjIntConsumer: 객체값과 정수형 인자값을 받이들이고 결과값을 리턴하지 않는다.

--> 결과

Java Class
JavaClass
14.0를 10으로 나눈 나머지는 4.0입니다.
Java8

[2] SupplierTest.java

package com.julian5383.ex01;

import java.util.function.IntSupplier;

public class SupplierTest {
	public static void main(String[] args) {
		IntSupplier intSupplier = () -> {
			int num = (int)(Math.random() * 6) + 1;
			return num;
		};
		
		int num = intSupplier.getAsInt();
		System.out.println("주사위 눈: " + num);
	}
}

- Supplier는 결과값을 리턴(공급)하는 역할을 한다. 코드문에서 쓰여면 인터페이스는 IntSupplier로 int값을 리턴한다.

- 추상메소드 getAsInt()를 이용해 결과값을 가지고 온다.

--> 결과

주사위 눈: 3

7) 람다 실전예제(Function 인터페이스, 람다이용)

[1] Student.java

package com.julian5383.ex01;

public class Student {
	private String name;
	private int englishScore;
	private int mathScore;
	
	public Student(String name, int englishScore, int mathScore) {
		super();
		this.name = name;
		this.englishScore = englishScore;
		this.mathScore = mathScore;
	}

	public String getName() {
		return name;
	}

	public int getEnglishScore() {
		return englishScore;
	}

	public int getMathScore() {
		return mathScore;
	}
	
	
}

[2] FunctionTest.java

package com.julian5383.ex01;

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

public class FunctionTest {
	private static List<Student> list=Arrays.asList(
			new Student("아이유",95,80),
			new Student("설운도",90,85)
	);
    //2개의 객체를 가지는 리스트 생성
	
	public static void printString(Function<Student, String> function) {
    		//Function 인터페이스를 구현한 객체를 만들고 객체의 레퍼런스 값을 function에 받아온다..
		//apply메소드에 Student 객체를 파라미터로 받고 String타입 문자열을 리턴한다.
		for(Student student: list) {
			System.out.print(function.apply(student) + " ");
		}
		System.out.println();
	}
	
	public static void printInt(ToIntFunction<Student> function) {
		for(Student student: list) {
			System.out.println(function.applyAsInt(student));
		}
		System.out.println();
	}
	//인터페이스 구현객체를 파라미터로 받는 메소드들
    
	public static void main(String[] args) {
		System.out.println("[학생이름]");
		printString(t -> t.getName());
        //Function<Student, String> function  = t->t.getName();
		//printString(function); //printString(t -> t.getName())을 풀어서 쓴것이다.
		
		System.out.println("[영어점수]");
		printInt(t -> t.getEnglishScore());
		
		System.out.println("[수학점수]");
		printInt(t -> t.getMathScore());
	}
}

--> 결과

[학생이름]
아이유 
설운도 

[영어점수]
95
90

[수학점수]
80
85

8) 람다 실전예제(구현 클래스 이용, 람다X)

[1] Student.java

package com.julian5383.ex01;

public class Student {
	private String name;
	private int englishScore;
	private int mathScore;
	
	public Student(String name, int englishScore, int mathScore) {
		super();
		this.name = name;
		this.englishScore = englishScore;
		this.mathScore = mathScore;
	}

	public String getName() {
		return name;
	}

	public int getEnglishScore() {
		return englishScore;
	}

	public int getMathScore() {
		return mathScore;
	}
	
	
}

[2] FunctionImpl.java

package com.julian5383.ex01;

import java.util.function.Function;

public class FunctionImpl implements Function<Student, String>{
	//인터페이스 구현 클래스 만들기
	@Override
	public String apply(Student t) {
		// TODO Auto-generated method stub
		return t.getName();
	}
	
}

[3] FunctionTest.java

package com.julian5383.ex01;

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

public class FunctionTest {
	private static List<Student> list=Arrays.asList(
			new Student("아이유",95,80),
			new Student("설운도",90,85)
	);
	//2개의 객체를 가지는 리스트 생성
	
	public static void printString(Function<Student, String> function) {
		//Function 인터페이스를 구현한 객체를 만들고 객체의 레퍼런스 값을 function에 받아온다..
		//apply메소드에 Student 객체를 파라미터로 받고 String타입 문자열을 리턴한다.
		for(Student student: list) {
			System.out.print(function.apply(student) + " ");
		}
		System.out.println();
	}
	
	public static void printInt(ToIntFunction<Student> function) {
		for(Student student: list) {
			System.out.println(function.applyAsInt(student));
		}
		System.out.println();
	}	
	//인터페이스 구현객체를 파라미터로 받는 메소드들
	
	public static void main(String[] args) {
		System.out.println("[학생이름]");		
		Function<Student, String> f = new FunctionImpl();
		printString(f);//구현 클래스로 객체를 만들고 그 객체 레퍼런스로 파라미터를 넘겨줌
		
		System.out.println("[영어점수]");
		printInt(t -> t.getEnglishScore());
		//student가 들어가면 영어점수가 나온다.
		
		System.out.println("[수학점수]");
		printInt(t -> t.getMathScore());
	}
}

--> 결과

[학생이름]
아이유 설운도 
[영어점수]
95
90

[수학점수]
80
85

9) 람다 실전예제(구현 클래스 이용, 구현 인터페이스 이용, 람다 X)

[1] Student.java

package com.julian5383.ex01;

public class Student {
	private String name;
	private int englishScore;
	private int mathScore;
	
	public Student(String name, int englishScore, int mathScore) {
		super();
		this.name = name;
		this.englishScore = englishScore;
		this.mathScore = mathScore;
	}

	public String getName() {
		return name;
	}

	public int getEnglishScore() {
		return englishScore;
	}

	public int getMathScore() {
		return mathScore;
	}
	
	
}

[2] MinjuInterface.java

package com.julian5383.ex01;

public interface MinjuInterface {
	public String minju(Student s);

}

[3] MinjuImpl.java

package com.julian5383.ex01;

public class MinjuImpl implements MinjuInterface{

	@Override
	public String minju(Student s) {
		// TODO Auto-generated method stub
		return s.getName();
	}

}

[4] FunctionTest.java

package com.julian5383.ex01;

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

public class FunctionTest {
	private static List<Student> list=Arrays.asList(
			new Student("아이유",95,80),
			new Student("설운도",90,85)
	);
	//2개의 객체를 가지는 리스트 생성
	
	public static void printString(Function<Student, String> function) {
		//Function 인터페이스를 구현한 객체를 만들고 객체의 레퍼런스 값을 function에 받아온다..
		//apply메소드에 Student 객체를 파라미터로 받고 String타입 문자열을 리턴한다.
		for(Student student: list) {
			System.out.print(function.apply(student) + " ");
		}
		System.out.println();
	}
	
	public static void printString2(MinjuInterface who) {
		for(Student student: list) {
			System.out.print(who.minju(student) + " ");
		}
		System.out.println();
	}
	
	public static void printInt(ToIntFunction<Student> function) {
		for(Student student: list) {
			System.out.println(function.applyAsInt(student));
		}
		System.out.println();
	}
	
	//인터페이스 구현객체를 파라미터로 받는 메소드들
	
	public static void main(String[] args) {
		System.out.println("[학생이름]");	
		MinjuInterface i = new MinjuImpl();
		printString2(i);
		
		System.out.println("[영어점수]");
		printInt(t -> t.getEnglishScore());
		//student가 들어가면 영어점수가 나온다.
		
		System.out.println("[수학점수]");
		printInt(t -> t.getMathScore());
	}
}

--> 결과

[학생이름]
아이유 설운도 
[영어점수]
95
90

[수학점수]
80
85