2023.05.17
● 추상클래스(abstract) 실습
1. animal 클래스
public abstract class Animal {
}
1) 멤버변수 :name
private String name; // 캡슐화
2) 생성자 : name을 매개변수로 가짐 (인테리어)
Animal(String name) { // setName의 역할(단, 한번만 설정 가능)
this.name = name;
}
3) 추상메소드 : bark() // 리턴값 없음
// 추상메소드 : 함수의 정의부만 선언
public abstract void bark(); // 자식클래스에서 상속받아서 구현(다형성)
4) name에 대한 읽기 속성함수
-> 추상클래스는 new 사용 안됨(메모리에 올리지 않음)
2. Dog 클래스(animal 상속)
public class Dog extends Animal{
}
1) 강아지의 종류를 멤버변수로 가짐
- 강아지의 종류 변수이름은 type으로 문자열
private String type;
2) 생성자 : name과 type을 매개변수로 가짐
Dog(String name, String type) {
super(name); // 부모생성자 호출 | super()는 기본생성자이기 때문에 호출 불가
this.type = type;
}
3) bark() 메소드 구현 : sysout ("멍멍!");
-> 함수본체 {} 활용
@Override
public void bark() {
System.out.println("멍멍!");
}
3. Cat 클래스(animal 상속)
1) 멤버변수 : age(정수형)
2) 생성자 : name, age를 매개변수로 가짐
3) bark() 메소드 구현 : sysout ("야옹~");
public class Cat extends Animal{
private int age;
Cat(String name, int age) {
super(name);
this.age = age;
}
@Override
public void bark() {
System.out.println("야옹~");
}
}
4. 실행클래스 - AnimalApp
1) 객체생성
public class AnimalApp {
public static void main(String[] args) {
Dog dog1 = new Dog("산", "요크셔테리어"); // 객체생성
dog1.bark();
dog1.run();
Cat cat1 = new Cat("크림", 5); // 객체생성
cat1.bark();
cat1.run();
}
2) 업캐스팅
-> Animal은 실체가 없으므로 업캐스팅
// 업캐스팅
Animal anidog = new Dog("보리", " 진돗개"); // Animal은 실체가 없으므로 업캐스팅
anidog.bark();
anidog.run(); // 5-2. Animal에 run() 추가
Animal anicat = new Cat("치즈", 10);
anicat.bark();
anicat.run();
-> 자식이 가진 메소드를 실행할 수 없음
5-1. 메소드 추가 : run()
- 각각의 Dog, Cat클래스에 메소드 만들기
5-2. 메소드 추가 : run()
- Animal 클래스에 메소드 만들기(public)
public void run() {
System.out.printf("%s는 한가로이 풀을 뜯고 있습니다.\n", name);
}
* 실행클래스에서 업캐스팅한 객체에 run 실행
Animal anicat = new Cat("치즈", 10);
anicat.run();
-> 가까운 메소드인 Cat에서의 run이 실행됨(디버그 확인)
* 기본생성자(매개변수가 없는 생성자)
- 사용자정의 생성자를 만들면 자동으로 생성되지 않음
* 다형성
- 인터페이스
- 추상메소드
* 싱글톤패턴(singletone)
객체의 인스턴스가 1개만 생성
= 생성자가 여러번 호출되어도 실제로 생성되는 객체는 하나
-> 기존의 인스턴스를 활용
보안적 측면에서 사용
* 최상위 클래스 : 오브젝트
-인터페이스는 개념에 가까움(객체지향으로 보면 최고 조상으로 보기도 함)
● 상속의 중요성
1. 자식클래스(Tiger) 만들기
public class Tiger extends Animal {
private String color;
Tiger(String name, String color) {
super(name);
this.color = color;
}
public void bark() {
System.out.println("어흥~!");
}
}
2. 자식클래스(Tiger)에서 run()을 구현하지 않는다면?
Tiger tiger1 = new Tiger("타노스", " 노란색");
tiger1.run();
Animal aniTiger = new Tiger("복순이", "흰색");
aniTiger.run();
-> 가까운 메소드인 부모클래스(Animal)가 가지고 있는 run()을 실행
@Override를 붙이는 것의 효과
- 부모가 가지고 있는 것을 고쳤다는 것을 알려줌(명시화 함)
- 기능에서는 차이가 없음
- 협업에서 중요하게 다뤄짐
6. Animal을 배열로 변경
향상된 for이용
System.out.println("\n-------배열-------");
Animal[] ani = new Animal[3];
ani[0] = new Dog("망치","치와와");
ani[1] = new Cat("깜냥이", 8);
ani[2] = new Tiger("복돌이", "황토색");
for (Animal s : ani){
System.out.println("동물의 이름은 : " + s.getName());
s.bark();
s.run();
System.out.println();
}
● 람다(Lambda)표현식
= 화살표함수
- 요즘 유행하는 함수형 프로그래밍의 기초시작
- 람다 표현식(lambda expression)이란 메소드를 하나의 식으로 표현한 것
- Lambda라는 이름은 수학에서 한 줄짜리 대수표현식
- 프로그래밍에서도 Lambda식(함수)은 익명함수 중에서 한 줄짜리 표현식으로 표현하는 방법
- 람다함수식의 기본 시작은 익명함수
- 익명함수는 함수의 이름이 없는 함수
- 바로 그 자리에서 정의하여 사용
- 람다함수는 그 중에서 줄수가 짧은 익명함수를 한 줄짜리 함수식으로 만든 함수
* 장점
- 가독성이 좋다
- 실제 함수의 정의생성, 호출이 없어짐으로 속도가 조금 빨라짐
* 메소드를 람다식으로 변경하기
// 메소드
int min(int x, int y) {
return x < y ? x : y; // 3항연산자
}
// 람다식
(x, y) -> x < y ? x : y;
- 같은 타입의 객체를 인자(x, y)로 받고 -> x < y ? x : y;을 리턴한다.
● Lambda 실습
1. 클래스에서 구현하기
1) 인터페이스 IMaxNumber
package inherit.lambda;
public interface IMaxNumber {
// 두 수를 입력받아서 가장 큰 수를 리턴
int getMaxNumber(int x, int y);
}
- 인터페이스는 public을 안적어도 public임
2) 구현클래스 MaxNumberImpl
~Impl : 인터페이스를 구현(implement)한 클래스임을 명시
package inherit.lambda;
public class MaxNumberImpl implements IMaxNumber {
// 메소드 구현
@Override
public int getMaxNumber(int x, int y) {
// 1. if else
if (x > y) {
return x;
} else if (x < y){
return y;
} else
return 0;
// 2. 클린코드 : if else의 연산자 버전(3항연산자)
return (x > y) ? x : y;
}
}
3) 실행클래스 MaxNumberApp
package inherit.lambda;
public class MaxNumberApp {
public static void main(String[] args) {
// 1. MaxNumber객체 생성
MaxNumberImpl maxNumber = new MaxNumberImpl();
// 2. 두 수중에서 큰값 알림
System.out.print("(100, 101)중 큰 수: ");
System.out.println(maxNumber.getMaxNumber(100, 101));
}
2-1. 익명함수를 이용하여 메인 클래스에서 구현하기(인터페이스)
4) 실행클래스 MaxNumberLambdaApp
- 클래스를 새로 만들지 않고 인터페이스 객체를 사용함
package inherit.lambda;
public class MaxNumberLambdaApp {
public static void main(String[] args) {
IMaxNumber maxNumber = new IMaxNumber() {
@Override
public int getMaxNumber(int x, int y) { // IMaxNumber는 인터페이스로 메소드를 구현해야함
return x > y ? x : y;
}
}; // 세미콜론이 왜 뒤에 붙는가 : 함수를 정의한 것이 아닌 실행문이라서, 문장의 끝은 ;를 적음
System.out.print("(100, 101)중 큰 수: ");
System.out.println(maxNumber.getMaxNumber(100, 101));
}
}
* 익명함수 : 재사용 불가(만든자리에서 사용해야함)
* 모든 클래스는 new를 사용하여 실체화 할 수 있다
-> 익명객체 필요 : 객체가 클래스 그 자체이므로 클래스를 따로 생성하지 않음 (본인 클래스이름이 없음)
* IMaxNumber는 인터페이스(실체화 불가)인데 new를 사용함 ?
- 익멱함수에서만 허용(그 자리에서만 사용하기 떄문) - 임시 이름
- 함수를 사용하면 사라짐
* 익명클래스
- 익명객체(클래스)를 구현하는 방법(2가지)
1. 부모/자식간 상속아래 부모클래스 이름으로 익명 자식객체(new)를 생성
2. 인터페이스를 구현한 인터페이스 이름으로 익명 구현객체(new)를 생성
-> 만들어진 클래스가 아니기 때문에 기존에 있는 클래스나 인터페이스명을 가지고 이를 상속(extends) 받거나 구현(implements)한 클래스를 만듦
▷ 위의 정규방식을 익명형식을 이용하는 방법으로 고치기
1. 인터페이스에 있는 메소드만 호출사용한다면 부모타입으로 업캐스팅을 하면 편리하다.
IMaxNumber imaxNumber = new MaxNumberImpl();
- 부모클래스와 관련없는 멤버들이 감추어지므로 보안면에서 유리
2. 익명형식 작성법
IMaxNumber imaxNumber = new IMaxNumber() {
public int getMaxNumber(int x, int y) {
return (x > y) ? x : y;
}
};
* 왼쪽(대입연산자= 기준)
1) 부모타입을 선언
= 상속을 해주는 타입(인터페이스 또는 클래스)변수로 선언하라는 의미
* 오른쪽(대입연산자= 기준)
1) 객체 생성(new = 방을 만들고 그 시작 주소를 리턴하라)
2) 부모타입(구현클래스의 이름이 없기 때문)
= 생성자라는 뜻으로 부모타입명()
* 코드블럭 구현
- 실행중인 상태에서 선언과 구현을 한 자리에서 다 하겠다.
- 익명클래스의 코드길이가 1~20줄 정도인 경우에 활용
- 객체를 생성 호출 사용을 생략함으로 실행속도를 올릴 수 있고 직관적 의미파악에 도움
- 장점 : 유지보수가 좋음 | 단점 : 코드가 너무 복잡하게 보임
1) 인터페이스의 메소드 구현이기때문에 - public
2) int형이라 return 필요
3) 문장의 끝에 세미콜론(;) 붙이기
2-2. 익명함수를 이용하여 메인 클래스에서 구현하기(추상클래스)
4) 실행클래스 MaxNumberLambdaApp
- 클래스를 새로 만들지 않고 추상화클래스 객채를 사용함
// 추상화 클래스
package inherit.lambda;
public abstract class AbstractMaxNumber {
// max값을 찾는 주메소드
abstract int getMaxNumber(int x, int y);
abstract void p(String msg);
void pl(String msg) {
p(msg + "\n");
}
}
// 3. 추상클래스(AbstractMaxNumber)를 사용한 익명함수 사용하기
// 부모클래스 선언 - 업캐스팅
AbstractMaxNumber aMaxNumber = new AbstractMaxNumber() {
public int getMaxNumber(int x, int y) {
if(x > y) {
return x;
} return y;
}
public void p(String msg) {
System.out.println(msg);
}
};
aMaxNumber.pl(
"max value " +
aMaxNumber.getMaxNumber(100, 200));
● 정규방식(익명함)을 람다함수로 변경하는 법
1. 제거 :
- 클래스 구현틀(new 부분) : lambda함수에 포함된 내용
- 메소드명 : 그 자리에서 바로 사용되기 때문에 함수를 생성할 필요 없음
- public
- 타입 (형식지정자)
- {} : 한줄이라 필요 없음
- return키워드 : lambda함수에 포함된 내용
- > 인터페이스에서 명시된 내용 제거
2. 매개변수와 함수본체가 남음
- 가독성을 위해 화살표(->) 사용
IMaxNumber rambdaMaxNumber =
// 인터페이스에 원형이 있어서 타입(형식지정자)을 생략해도 됨
(x, y) -> (x > y) ? x : y; // 한 줄 식(=getMaxNumber 구현식)
System.out.println("람다함수를 이용한 구현\n");
System.out.println(
"max number = " +
rambdaMaxNumber.getMaxNumber(100, 200));
}
* 공유폴더에 람다에 대한 설명 공유
- 이름을 붙이지 않기 때문에 식별이 어려움
람다함수 규칙
1. 람다는 익명객체로부터 나옴
- 익명객체에 나온 구조를 따라야함
2. 람다식은 함수적 인터페이스인 경우에만 사용 가능
- 함수적 인터페이스 : 인터페이스가 단 한개의 추상 메소드를 정의하고 있는 인터페이스
3. 어노테이션(@FunctionalInterfce)을 통해 오류방지
4. 람다식표현법
- 매개변수와 실행문으로 구성
() -> {}