본문 바로가기
백엔드/자바

자바 - 15

by study_yeon 2023. 5. 18.

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. 람다식표현법
- 매개변수와 실행문으로 구성
     () -> {}

 

'백엔드 > 자바' 카테고리의 다른 글

자바 - 17  (0) 2023.05.21
자바 - 16  (0) 2023.05.19
자바 - 14  (0) 2023.05.17
자바 - 13  (0) 2023.05.16
자바 - 12  (0) 2023.05.14