재밌고 어려운 IT를 이해해보자~!
추상클래스 (abstract class), 인터페이스 (Interface) 본문
오늘은 완전 추상적인 녀석을 배웠다!
추상클래스에 대해선 학문적으로는 알면 알수록 정보가 무지많은데,
개발업계에선 그렇게 많이 쓰지는 않는다.
따라서 정보를 찾을땐 학문관련인지 개발관련인지 알아보고 받아들이자.
추상클래스 어떻게 등장했는가 ?!
기존에 이미 많은 자식 클래스들이 있었는데, 이들의 부모 클래스가 없어서
[부모 클래스]를 정의하기위해 탄생
▶ 객체를 정의하는데에는 관심 X
객체생성이 불가능 -> 객체생성을 안할 클래스들한테 붙인다.
EXAMPLE
[추상]포켓몬- [추상\전기,물 - 피카츄,꼬부기
-> 포켓몬,전기,물 객체는 없다 -_-
[추상]챔피언-티모,아리,아무무
-> 챔피언이라는 객체는 없다 티모만있을뿐...
ex) [추상]모양-원,네모,세모,별...
ex) [추상]동물-강아지,고양이,사슴,알파카...
ex) 사람-학생,직원 -> 사람은 객체가 있다!!!!!!!! 상위중에 추상이 아닐수도 있는것이 존재한다.
또한 저번에 팀프로젝트로 작성했던 자동차 관리 프로그램에서
우리는 부모클래스로 일반자동차를 만들고 자식클래스로 슈퍼카와 전기차를 만들었다.
이때, 일반자동차는 객체가 존재할 수 있음으로. 추상을 넣으면 안된다.
ex) 자동차-슈퍼카,전기차
▶즉 추상클래스는 클래스를 정의하는데에 관심이 있다. !!!!!!!!★★★
추상클래스를 사용하는 이유
1. 불필요한 객체화를 막기위함 (깡제)
2. 추상메서드를 사용하기 위함
▶ 메서드 오버라이딩 (강제)
두가지의 강제성이 생김으로 인해서 코드가 더욱 일관성 있어진다.
추상클래스에만 추상메서드가 존재 할 수 있으며 추상메서드는
메서드 바디가 존재하지 않는다.
abstract void printInfo(); 추상메서드
▶ 얘 좀 오버라이딩 해줘.
▶ 오버라이딩 "강제"
▶ 호출될일이 없으므로 {}에 메서드바디가 없음
추상클래스를 사용한 포켓몬
package class01;
abstract class Pokemon {
int lv;
String name;
Pokemon(int lv) {
this.lv = lv;
}
abstract void attack();
abstract void hello();
void printInfo() { //세상모든포켓몬은 같은출력을한다 오버라이딩 X
System.out.println(this.name + " Lv" + this.lv);
}
}
class Elec extends Pokemon {
Elec(int lv) {
super(lv);
}
@Override
void attack() {
System.out.println("백만볼트!!!");
}
@Override
void hello() {
}
}
// name을 생성자를 만들때마다 초기화해줘야하는 불편함 발생.
// 따라서 name도 부모클래스에서 받아와
class Pica extends Elec {
Pica() {
this(5);
this.name = "피카츄";
}
Pica(int lv) {
super(lv);
this.name = "피카츄";
}
@Override
void hello() {
System.out.println("피카피카");
}
}
class Ry extends Elec {
Ry() {
this(5);
this.name = "라이츄";
}
Ry(int lv) {
super(lv);
this.name = "라이츄";
}
@Override
void hello() {
System.out.println("라이라이");
}
}
public class Test04 {
public static void main(String[] args) {
Pica p1 = new Pica();
Pica p2 = new Pica(11);
Ry r1 = new Ry();
p1.printInfo();
r1.printInfo();
p2.printInfo();
p1.attack();
r1.attack();
p1.hello();
r1.hello();
}
}
현재 보면 피카츄,라이츄 name을 반복적으로 초기화를 시켜주게 코딩이 되어있다.. -> this생성자를 쓴곳에 name은 초기화가 필요 없었다.. 아무튼 별로인 코드
부모클래스에 생성자를 lv만 받는걸로 선언해서 생긴일, 좋지않은 코드가 되었다.
앞으로는 좀더 생각해서 부모클래스의 생성자를 선언해주자.
★ ★ ★ 부모클래스 멤버변수 모두를 파라미터로 가지고 있는 생성자를 선언하고 시작하자! ★ ★ ★
그러면 강제성도 높아지고 코드효율도 좋아진다.
인터페이스
메서드 시그니쳐를 통일하면 어떨까~?? 하다가 나온것이 합의해서 인터페이스화 하여 활용하는 것이다.
인터페이스 특징
1. 클래스가 아님
2. 갖고있는 모든 메서드가 public abstract 가 붙는다. 즉 추상메서드
3. implement키워드로 사용.
인터페이스~~~~~~~~를 활용한 과제
SamsungTV
String userName; // 사용자명
int channel; // 현재 채널 위치
void channelUp(); // channel++;
void channelDown(); // channel--;
void channelRandom(); // 1~999로 채널 랜덤 변경됨(현재 위치 제외)
void printInfo(); // ㅁㅁ님의 현재 시청 채널은 ㅇㅇ입니다.
SamsungMiniTV
int battery;
void channelRandom(); // 1~999로 채널 랜덤 변경됨(현재 위치 제외)
단, 배터리 10씩 소모
0이하가 되면 채널 랜덤 변경 불가능
LgTV
boolean power;
int channel;
void channelUp(); // ON 상태일때 : channel++;
void channelDown(); // ON 상태일때 : channel--;
void printInfo(); // ON 상태일때 : 현재 시청 채널은 ㅇㅇ입니다.
// OFF 상태일때 : 전원이 꺼져있습니다...
main()에서
SamsungTV tv1=new SamsungTV("홍길동"); // 처음 채널 위치는 1로 고정
SamsungMiniTV tv2=new SamsungMiniTV("홍길동"); // 처음 채널 위치는 1로 고정. 처음 배터리는 50~70 랜덤 설정됨
LgTV tv3=new LgTV(); // 처음 채널 위치는 1로 고정. 처음 전원은 OFF 상태
를 구현해주세요.
인터페이스는 어떤 모습이어야할지 고민해보세용! :D
★ 한 글 코 딩 절 대 해 줘
★ 문제보다 더 많이 만들기 xxxxxxxxxx
★ 문제 의도대로 코딩하기 O
★ ★ ★ 한코글딩 ★ ★ ★
삼성티비 엘지티비에서 곂치는 메서드 파악 후 인터페이스화
인터페이스 {
void channelUp();
void channelDown();
void printInfo();
}
인터페이스를 implements하여 오버라이딩하자 ...
Class SamsungTV implements 인터페이스 {
String 사용자명
int 채널
//이름만 받았을때 채널 1로 고정 -> 이름만 받는 생성자 생성
생성자 (String 사용자명) {
this(사용자명,1) // 생성자 오버로딩
}
생성자 (String 사용자명, int 채널) {
this 사용자명 = 사용자명
this 채널 = 채널
}
//인터페이스 메서드 오버라이딩
@오버라이딩
void channelUp() {
this.채널++
}
@오버라이딩
void channelDown(){
this.채널--
}
@오버라이딩
void printInfo() {
(ㅁㅁ님의 현재 시청 채널은 ㅇㅇ 입니다.)
}
void channerRandom() {
int num = and.nextInt(998) +1
while (true) {
if (num != this.채널) {
this.채녈 = num
break;
}
}
}
class SamsungMiniTV extends SamsungTV {
int 베터리 = 50~70랜덤
SamsungMiniTV(사용자명) {
super(사용자명, 1); //부모클래스 멤버변수 초기화
}
@오버라이딩
void channerRandom() {
int num = and.nextInt(998) +1
while (true) {
if (num != this.채널) {
this.채녈 = num
break;
}
}
this 베터리 -= 10;
if (베터리 <0) {
(베터리 상태 + this.베터리 채널 랜덤 변경 불가능)
}
}
Class SamsungTV implements 인터페이스 {
boolean 파워 = false;
int 채널
//인터페이스 메서드 오버라이딩
@오버라이딩
void channelUp() {
this.채널++
}
@오버라이딩
void channelDown(){
this.채널--
}
@오버라이딩
void printInfo() {
(ㅁㅁ님의 현재 시청 채널은 ㅇㅇ 입니다.)
}
void powerOnOff {
if(!파워) {
파워 ON
파워 = true
}
else {
파워 OFF
파워 = false
}
}
}
main {
SamsungTV tv1=new SamsungTV("홍길동"); // 처음 채널 위치는 1로 고정
SamsungMiniTV tv2=new SamsungMiniTV("홍길동"); // 처음 채널 위치는 1로 고정. 처음 배터리는 50~70 랜덤 설정됨
LgTV tv3=new LgTV();
tv1.channerUp();
tv1.channerUp();
tv1.channerUp();
tv1.channerDown();
tv1.channerDown();
tv1.printInfo();
tv1.channelRandom();
tv1.printInfo();
tv2.channelRandom();
tv2.channelRandom();
tv2.channelRandom();
tv2.channelRandom();
tv1.printInfo();
tv1.printInfo();
tv2.channelRandom();
tv1.channelRandom();
if(!tv3.power) {
(전원이 꺼져있습니다.)
}
else {
tv3.channerUp();
tv3.channerDown();
tv3.channerDown();
tv3.printInfo();
}
tv3.powerOnOff();
if(!tv3.power) {
(전원이 꺼져있습니다.)
}
else {
tv3.channerUp();
tv3.channerDown();
tv3.channerDown();
tv3.printInfo();
}
}
실제코딩
삼성, LG에 적용하기위한 인터페이스 작성
package class04;
public interface TVinterface {
void channelUp();
void channelDown();
void printInfo();
}
삼성TV 엘지TV에 동일하게 필요한 메서드만 인터페이스화 시켜서 오버라이딩을 통해 작성했다.
메인
package class04;
import java.util.Random;
import java.util.Scanner;
class SamsungTV implements TVinterface {
String userName;
int channel;
SamsungTV(String userName) {
this(userName, 1);
}
SamsungTV(String userName, int channel) {
this.userName = userName;
this.channel = channel;
}
@Override
public void channelUp() {
this.channel++;
System.out.println("채널+1");
}
@Override
public void channelDown() {
this.channel--;
if (this.channel < 0) {
this.channel = 0;
}
System.out.println("채널-1");
}
public void channelRandom() {
Random rand = new Random();
int num = rand.nextInt(998) + 1;
while (this.channel != num) {
this.channel = num;
break;
}
System.out.println("채널 랜덤");
}
@Override
public void printInfo() {
System.out.println(this.userName + " 님의 현재 시청 채널은 " + this.channel + " 입니다.");
}
}
class SamsungMiniTV extends SamsungTV {
Random rand = new Random();
int battery = rand.nextInt(21) + 50;
SamsungMiniTV(String userName) {
super(userName, 1);
}
@Override
public void channelRandom() {
if (this.battery > 0) {
super.channelRandom();
this.battery -= 10;
System.out.println("베터리 10 감소");
} else {
System.out.println("베터리 마이너스 -_-!! 랜덤변경 불가능");
}
}
@Override
public void printInfo() {
System.out.println(this.userName + " 님의 현재 시청 채널 :" + this.channel + " 베터리 :" + this.battery);
}
}
class LgTV implements TVinterface {
boolean power = false;
int channel;
@Override
public void channelUp() {
this.channel++;
System.out.println("채널 증가");
}
@Override
public void channelDown() {
this.channel--;
if (this.channel < 0) {
this.channel = 0;
}
System.out.println("채널 감소");
}
public void turnOnOff() {
if (!power) {
System.out.println("TV ON!");
this.power = true;
} else {
System.out.println("TV OFF");
this.power = false;
}
}
public boolean checkturnOnOff() {
if (!this.power) {
System.out.println("현재 TV상태 OFF 켜주세요");
return this.power;
} else {
return true;
}
}
@Override
public void printInfo() {
System.out.println("현재 시청 채널은 " + this.channel + "입니다.");
}
}
public class Test01 {
public static void main(String[] args) {
SamsungTV tv1 = new SamsungTV("홍길동");
SamsungMiniTV tv2 = new SamsungMiniTV("홍길동");
LgTV tv3 = new LgTV();
while (true) {
Scanner sc = new Scanner(System.in);
System.out.println("1.삼성 2.삼성미니 3.엘쥐");
System.out.println("영원히 못나갑니다");
int tv = sc.nextInt();
if (tv == 1) {
System.out.println("삼성TV 조작중");
System.out.println("1.채널up 2.채널down 3.채널random 4.정보출력 5.exit");
A: while (true) {
switch (sc.nextInt()) {
case 1:
tv1.channelUp();
break;
case 2:
tv1.channelDown();
break;
case 3:
tv1.channelRandom();
break;
case 4:
tv1.printInfo();
break;
case 5:
break A;
}
}
}
else if (tv == 2) {
System.out.println("삼성미니TV 조작중");
System.out.println("미니TV는 베터리를 신경써야합니다.");
System.out.println("현재상태");
tv2.printInfo();
System.out.println("1.채널up 2.채널down 3.채널random 4.정보출력 5.exit");
A: while (true) {
switch (sc.nextInt()) {
case 1:
tv2.channelUp();
break;
case 2:
tv2.channelDown();
break;
case 3:
tv2.channelRandom();
break;
case 4:
tv2.printInfo();
break;
case 5:
break A;
}
}
}
else if (tv == 3) {
System.out.println("LgTV 조작중");
System.out.println("1.채널up 2.채널down 3.정보출력 4.TV켜기 5.exit");
A: while (true) {
switch (sc.nextInt()) {
case 1:
if (tv3.checkturnOnOff()) {
tv3.channelUp();
}
break;
case 2:
if (tv3.checkturnOnOff()) {
tv3.channelDown();
}
break;
case 3:
if (tv3.checkturnOnOff()) {
tv3.printInfo();
}
break;
case 4:
tv3.turnOnOff();
break;
case 5:
if (tv3.checkturnOnOff()) {
tv3.turnOnOff();
}
break A;
}
}
}
}
}
}
상속과 인터페이스 는 같이 쓸수 있다...
expends, implements.
//강사님코드
interface
package class01;
public interface TVimpl {
public static final int CMIN=1;
int CMAX=999;
void channelUp();
void channelDown();
void printInfo();
}
main
package class01;
import java.util.Random;
class SamsungTV implements TVimpl{
String userName;
int channel;
static Random rand=new Random();
// 반복문 횟수 -> 메서드 호출 횟수 -> 객체 생성 횟수 -> 1번
// "싱글톤 패턴" 유지 ★
SamsungTV(String userName){
this.userName=userName;
this.channel=1;
}
@Override
public void channelUp() {
this.channel++;
if(this.channel>TVimpl.CMAX) {
this.channel=TVimpl.CMIN;
}
}
@Override
public void channelDown() {
this.channel--;
if(this.channel<TVimpl.CMIN) {
this.channel=TVimpl.CMAX;
}
}
public void channelRandom() {
int randNum;
while(true) {
randNum=SamsungTV.rand.nextInt(TVimpl.CMAX-TVimpl.CMIN+1)+TVimpl.CMIN; // ※ 가능하다면 new는 적게 사용하는것이 좋다!
if(this.channel!=randNum) {
break;
}
}
this.channel=randNum;
}
@Override
public void printInfo() {
System.out.println(this.userName+"님의 현재 시청 채널은 "+this.channel+"입니다.");
}
}
class SamsungMiniTV extends SamsungTV{
int battery;
final static int BM=10;
// 상수를 목적으로 사용하는 변수는 전부 대문자
SamsungMiniTV(String userName){
super(userName);
this.battery=SamsungTV.rand.nextInt(21)+50; // 50~70
}
@Override
public void channelRandom() {
if(this.battery<SamsungMiniTV.BM) { // ※ 하드코딩
System.out.println(" 로그 : 배터리 부족으로 채널 랜덤 변경 불가능");
}
super.channelRandom();
this.battery-=SamsungMiniTV.BM;
}
}
class LgTV implements TVimpl{
boolean power;
int channel;
LgTV(){
this.power=false;
this.channel=1;
}
@Override
public void channelUp() {
if(this.power) {
// 기능
}
else {
System.out.println(" 로그 : 전원 OFF 상태");
}
}
@Override
public void channelDown() {
// ※ 기능을 먼저 작성하고, 안될때의 코드를 위에 배치
if(!this.power) {
System.out.println(" 로그 : 전원 OFF 상태");
return;
}
// 기능
}
@Override
public void printInfo() {
// TODO Auto-generated method stub
}
}
public class Test01 {
public static void main(String[] args) {
SamsungTV tv1=new SamsungTV("홍길동"); // 처음 채널 위치는 1로 고정
SamsungMiniTV tv2=new SamsungMiniTV("홍길동"); // 처음 채널 위치는 1로 고정. 처음 배터리는 50~70 랜덤 설정됨
LgTV tv3=new LgTV(); // 처음 채널 위치는 1로 고정. 처음 전원은 OFF 상태
}
}
배워야할점
1.
심화 가능하다면 new는 적게 사용하는 것이 좋다.
일단 while문 밖으로 뺴고
class 안에 Random 선언해서
객체가 생성되었다면 rand를 생성하고 rand가호출되면 그냥 갔다가 쓴다.
class () {
Radnom rand;
}
SamsungTV() {
this.rand=new randdom();
}
더좋은방법~
생성자에서 -> 삼성티비클래스로 옮기기
class SamsungTV() {
static Random rand = new Random();
}
반복문횟수 -> 메서드 호출 횟수 -> 객체 생성 횟수 -> 1번 으로 줄었다.
"싱글톤 패턴"이 유지된다.★★★★★★★★★ 디자인 패턴이다.
줄일수있는 상황에서 최대로 줄인것.
2.
반복, if문에서
기능을 먼저 작성하고, 안될떄의 코드를 위에 배치
하드코딩은 최소화하기.
booolean power; 일때
if(power) 하면 true ....
3.
인터페이스에는 변수선언을 못한다 항시 상수
public static final 이 숨어있음.
4.
void 메서드에 return문?
=> 아래와 같이 반환값이 없는 void 메소드에서도 reutrn 문을 사용하면 바로 해당 메소드 스택을 빠져나갈 수 있다!!!
public void channelDown() {
// ※ 기능을 먼저 작성하고, 안될때의 코드를 위에 배치
if(!this.power) {
System.out.println(" 로그 : 전원 OFF 상태");
return;
}
'코리아IT핀테크과정' 카테고리의 다른 글
MVC design pattern (0) | 2023.12.12 |
---|---|
컬렉션 프레임워크 (Collection Framework) (2) | 2023.12.07 |
상속을 이용한 CRU 설계 및 제작 (0) | 2023.12.03 |
상속(Inheritance) (2) | 2023.12.01 |
this 예약어, 로그출력, 클래스와 배열의 활용 (0) | 2023.12.01 |