빅데이터 Java 개발자 교육 - 07일차 상속(inheritence) [(extend),(interface),(implements)]

Jun_Gyu·2023년 2월 25일
0
post-thumbnail

기존에 이용했던 import는 다른곳에 저장되어있는 클래스를 불러와서 사용하는 형식이었다.

하지만 이번에는 조금 다른 방식으로 다른 클래스를 이용해보려고 한다.

자바에는 "상속" 이라는 개념이 존재한다. 상위클래스(부모클래스)의 내용들을 하위클래스(자식클래스)에게 물려준다는 뜻으로, 하위클래스는 상위클래스에 있는 멤버들을 그대로 사용하거나 수정하여 사용할 수 있게 된다.

하지만 상속이 되지 않는 경우가 몇가지 존재하는데,

1. 부모 클래스의 private 접근 제한을 갖는 필드 및 메소드는 자식이 물려받을 수 없다.

2. 부모와 자식 클래스가 서로 다른 패키지에 있다면, 부모의 default 접근 제한을 갖는 필드 및 메소드도 자식이 물려받을 수 없다.
(default 접근 제한은 ‘같은 패키지에 있는 클래스’만 접근이 가능하게끔 하는 접근 제한자이기 때문이다.)

그 이외의 경우는 모두 상속의 대상이 된다. 출처

상속을 하는 이유는 이미 만들어진 클래스를 사용하기 때문에 개발을 하는데 있어 시간과 메모리 자원 모두를 아낄 수 있기 때문이다.

아래의 예를 들어보겠다.

package day7;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Parents {
	
	private String name = null; //클래스로 되어있는것들은 문자, 숫자 모두 null로 지정가능
	private int age = 0;
	private Integer age1 = null;

	public Parents() {
		super();
		
	}
	public void work() {
		System.out.println("parent work");
	}
	public void study() {
		System.out.println("parent study");
	}
}

Parents라는 클래스를 만들고,

package day7;

public class Main {

	public static void main(String[] args) {
		Parents p = new Parents();
		
		//메소드는 객체변수. 메소드호출();
		p.work();
		p.study();
	}
}

메인클래스에서 다음과 같이 메소드를 호출하면

위와 같이 출력이 된다. 만약 Parents 클래스의 기능을 상속 받으려면 어떻게 해야할까??

방법은 간단하다.

먼저 새로운 Child1이라는 클래스를 만들도록 하겠다.

package day7;

public class Child1 extends Parents{
}

대신 Child1이라는 클래스명 뒤에

[ extends "상속받으려는 클래스명" ]으로 덧붙여 써준다. 여기서는 부모클래스를 상속받으려 하니 extends parents 라고 덧붙여주면 되겠다.

이후 메인클래스에서

package day7;

public class Main {

	public static void main(String[] args) {
		
		Child1 c1 = new Child1();
		c1.work();
		c1.study();
	}
}

와 같이 입력 시 실행하면

Child1 클래스로 상속이 잘 이루어진것을 볼 수 있다.

그렇다면 부모클래스의 내용을 인위적으로 바꾸려면 어떻게 해야할까?

바로 @Override 를 사용하는 방법이다.

사용하려는 클래스의 바탕화면에 우클릭 이후 위의 경로로 들어가게 되면

위와 같은 화면이 나타나는것을 알 수 있다. 우리는 study와 work 메소드를 사용할것이기 때문에 study(), work()를 찾아서 체크를 해준다.

그렇게 되면

package day7;

public class Child2 extends Parents {

	@Override
	public void work() {
		// 부모의 work() 메소드를 호출함.
		super.work();// 부모의 기능을 사용함.
	}

	@Override
	public void study() {
		super.study();
	}
}

위처럼 @Override를 통해서 Parents클래스의 메소드 기능들을 들고오게 된다.

여기서 보이는 super."~"(); 이라는 객체가 부모클래스에서의 메소드정보를 가져오는것인데,

이를 지우고

package day7;

public class Child2 extends Parents {

	@Override // 이 메소드는 기능을 재 정의 할것임.
	public void work() {
		// super.work(); (부모기능)을 다 빼버리고 새롭게 사용.
	    System.out.println("child2 work");
	}

	@Override
	public void study() {
	    System.out.println("child2 study");
	}
}

위처럼 새로 넣고자 하는 기능들을 넣어 실행을 시키게 되면

사진처럼 새롭게 추가된 기능을 정상적으로 수행하는것을 볼 수 있다. 이러한 기능을 바탕으로 또 다른 예제를 구성해보겠다.

이번에는 일반 클래스가 아닌 'MyListener' 인터페이스를 구성해보자.

package day7;

public interface MyListener {

	// 기능만 정의함
	public void work();

	public void study();
}

이번에는 이전과 다르게 부모클래스명 앞에다 "interface"라고 추가를 하였다. 부모클래스에서 interface를 추가하게 되면 상속받으려는 자식클래스에는 무조건적으로 "implements"를 붙여줘야한다.

말 그대로 기능만 정의해둔 상태이다. 이 인터페이스를 사용하여 '상속'클래스를 만들어 기능을 정의해보겠다.

package day7;

public class MyInter1 implements MyListener {

	@Override
	public void work() {
		System.out.println("myinter1 = > work");
	}

	@Override
	public void study() {
		System.out.println("myinter1 = > study");
	}
}
package day7;

public class MyInter2 implements MyListener {

	@Override
	public void work() {
		System.out.println("myinter2 = > work");
	}

	@Override
	public void study() {
		System.out.println("myinter2 = > study");
	}
}

MyInter1,2 이렇게 두가지의 클래스를 만들었다.

그리고

package day7;

public class Print {

	public void action(MyListener m) {
		m.work();
		m.study();
	}
}

출력을 위한 클래스까지 구성하였다. 이를 바탕으로 메인 클래스에서

package day7;

public class Main {

	public static void main(String[] args) {

		MyListener ml = new MyInter1();
		MyListener m2 = new MyInter2();

		Print p = new Print();
		p.action(m2);
       }
}

임의로 MyInter2 클래스를 출력하도록

코드를 구성하였을때, 결과값은

로 출력된다.

지난시간 사용했던 swing또한 적용되는 부분이다.

package day7;

import java.awt.HeadlessException;

import javax.swing.JFrame;

public class Frame1 extends JFrame {

	public Frame1() throws HeadlessException {
		super();// 부모의 생성자 호출
		// windowbuild를 이용해서 드래그하면 자동으로 코드가 생성되는 툴

		// swing 실습때 "2줄 넣으세요" 했던 그 두줄.
		this.setSize(300, 500);
		this.setVisible(true);
	}
}

위의 방식대로 창의 사이즈를 정하고, 코드 실행시 보이도록 설정하여

new Frame1();

로 실행하게되면

300x500 사이즈의 창이 뜨게 된다.

이쯤에서 간단하게 오늘 배운 내용들을 요약하고 넘어가보자.



  • extends는 일반 클래스와 abstract 클래스 상속에 사용되고, implement는 interface 상속에 사용된다.

  • class가 class를 상속받을 땐 extends를 사용하고, interface가 interface를 상속 받을 땐 extends를 사용한다.

  • class가 interface를 사용할 땐 implements를 써야하고

  • interface가 class를 사용할 땐 implements를 쓸수 없다.

  • extends는 클래스 한 개만 상속 받을 수 있다.

  • extends 자신 클래스는 부모 클래스의 기능을 사용한다.

  • implements는 여러개 사용 가능하다.

  • implements는 설계 목적으로 구현 가능하다.

  • implements한 클래스는 implements의 내용을 다 사용해야 한다.


그리하여 오늘은 위의 내용을 이용해서 MongoDB에 자료가 저장이 될 수 있도록 실습을 통해서 코드를 직접 구성해보았다.

먼저 설계도면의 역활을 할 인터페이스 클래스이다.

package day7;

import java.util.List;

public interface BoardDAO {

	// 게시글 추가
	public int insertBoard(Board board);

	// 게시글 수정
	public int updateBoard(Board board);

	// 게시글 삭제
	public int deleteBoard(long no);

	// 전체 조회
	public List<Board> selectBoardList();

	// 게시글 수에 따른 조회 ex)n보다 큰 경우
	public List<Board> selectBoardHitList(long hit);

	// 게시글 1개 조회
	public Board selectBoardOne(long no);

}

대략적으로 어떠한 기능을 할 것인지에 대해서 크게 적어두고 각 메소드에 대해서 따로 변수를 선언하지 않은 모습이다.

다음으로는 implements 클래스이다.

package day7;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.bson.Document;
import org.bson.conversions.Bson;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.UpdateResult;

public class BoardDAOImpl implements BoardDAO {

	// 아이디:암호@서버주소:포트번호/DB명
	final String url = "mongodb://id209:pw209@1.234.5.158:37017/db209";
	private MongoCollection<Document> boardColl = null;
	private MongoCollection<Document> seqColl = null;
	/*----------------------------------------------------------------------------*/
	// DB접속 & 접속여부 출력
	public BoardDAOImpl() {
		try {
			// 설계도면 객체 = 클래스명.정적메소드()
			// 정적메소드는 객체가 1개만 생성되기때문에 리소스 낭비가 없다.
			MongoClient client = MongoClients.create(url);
			if (client != null) {
				this.boardColl = client.getDatabase("db209").getCollection("boards");
				this.seqColl = client.getDatabase("db209").getCollection("sequence");
				System.out.println("DB접속 성공.");
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("DB접속 실패.");
		}
	}
	/*----------------------------------------------------------------------------*/
	// 글제목, 내용, 작성자 정보만 전달됨.
	// 글번호(시퀀스), 조회수 1, 등록일자는 현재시간
	@Override
	public int insertBoard(Board board) {
		try {
			// 시퀀스를 이용한 idx 값을 받고 idx를 1 증가 시키기
			Bson filter = Filters.eq("_id", "SEQ_BOARD_NO");
			Bson update = Updates.inc("idx", 1);
			// 초기값 1을 가져오고 2로 바꿔줌. (findOneAndUpdate)
			Document doc = this.seqColl.findOneAndUpdate(filter, update);
			// 받은 idx값으로 글번호를 설정하기
			board.setBrdNo(doc.getLong("idx"));
			board.setBrdHit(100L);
			board.setBrdDate(new Date());
			// Board 타입의 값들을 Document로 복사하세요.
			Document doc1 = new Document();
			doc1.append("_id", board.getBrdNo());
			doc1.append("title", board.getBrdTitle());
			doc1.append("content", board.getBrdContent());
			doc1.append("writer", board.getBrdWriter());
			doc1.append("hit", board.getBrdHit());
			doc1.append("reg date", board.getBrdDate());

			InsertOneResult result = this.boardColl.insertOne(doc1);
			System.out.println(result);
			if (result.getInsertedId().asInt64().getValue() == board.getBrdNo()) {
				return 1; // 정확하게 데이터가 추가된 경우 1을 반환
			}
			return 0; // 실행은 되었으니 추가하지 못한 경우에 0을 반환.
		} catch (Exception e) {
			e.getStackTrace();
			return -1;
		}
	}
	/*----------------------------------------------------------------------------*/
	// 업데이트
	// 글번호와 제목, 내용, 작성자를 전송하면
	// 해당글번호의 제목, 내용, 작성자를 변경하기
	@Override
	public int updateBoard(Board board) {
		try {
			// 변경하고자 하는 항목의 조건
			Bson filter = Filters.eq("_id", board.getBrdNo());
			// 변경할 항목들 "제목, 내용, 작성자"
			Bson update1 = Updates.set("title", board.getBrdTitle());
			Bson update2 = Updates.set("content", board.getBrdContent());
			Bson update3 = Updates.set("writer", board.getBrdWriter());

			// updateOne (조건, 변경값) => 변경값이 하나의 Bson에만 가능.. combine
			Bson update = Updates.combine(update1, update2, update3);
			UpdateResult result = this.boardColl.updateOne(filter, update);
			if (result.getModifiedCount() == 1) {
				return 1;
			}
			return 0;
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
	}
	/*----------------------------------------------------------------------------*/
	// 삭제하기
	// 글 번호가 전달되면 1개 삭제하기
	@Override
	public int deleteBoard(long no) {
		try {
			Bson filter = Filters.eq("_id", no);
			DeleteResult result = this.boardColl.deleteOne(filter);
			System.out.println(result.toString());
			if (result.getDeletedCount() == 1L) {
				return 1;
			}
			return 0;
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		}
	}
	/*----------------------------------------------------------------------------*/
	// 전체 글조회-
	@Override
	public List<Board> selectBoardList() {
		// Board타입을 n개 보관할 수 있는 동적배열
		List<Board> list = new ArrayList<Board>();
		try { // 정상동작
				// 글 번호를 기준으로 내림차순(-1) 오름차순(1)
			Bson sort = Filters.eq("_id", -1);
			// MongoCursor<Document> == ArrayList<Document>
			MongoCursor<Document> docs = this.boardColl.find().sort(sort).cursor();
			while (docs.hasNext()) {
				Document doc = docs.next(); // 1개 꺼내기 (전체 갯수 1 감소함.)
				Board board = new Board();
				board.setBrdNo(doc.getLong("_id"));
				board.setBrdTitle(doc.getString("title"));
				board.setBrdContent(doc.getString("content"));
				board.setBrdWriter(doc.getString("writer"));
				board.setBrdHit(doc.getLong("hit"));
				board.setBrdDate(doc.getDate("reg date"));
				// Document => Board 복사
				list.add(board);
			}
			return list;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
    // hit 이하의 게시물만 조회 (lte)-
	@Override
	public List<Board> selectBoardHitList(long hit) {
		List<Board> brdList = new ArrayList<>();

		try {
			Bson filter = Filters.lte("hit", hit);
			MongoCursor<Document> list = this.boardColl.find(filter).sort(Filters.eq("hit", -1)).iterator(); // hit 기준으로
																												// 내림차순

			while (list.hasNext()) {
				Document doc = list.next();
				Board board = new Board();
				board.setBrdNo(doc.getLong("_id"));
				board.setBrdTitle(doc.getString("title"));
				board.setBrdContent(doc.getString("content"));
				board.setBrdWriter(doc.getString("writer"));
				board.setBrdHit(doc.getLong("hit"));
				board.setBrdDate(doc.getDate("date"));

				brdList.add(board);
			}
		} catch (Exception e) {
			e.printStackTrace();
			brdList = null;
		}
		return brdList;

	}

	// 보드 선택하기-
	@Override
	public Board selectBoardOne(long no) {
		try {
			Bson filter = Filters.eq("_id", no);
			Document doc = this.boardColl.find(filter).first();

			Board board = new Board();
			board.setBrdNo(doc.getLong("_id"));
			board.setBrdTitle(doc.getString("title"));
			board.setBrdContent(doc.getString("content"));
			board.setBrdWriter(doc.getString("writer"));
			board.setBrdHit(doc.getLong("hit"));
			board.setBrdDate(doc.getDate("reg date"));

			return board;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	/*----------------------------------------------------------------------------*/
	// 답글의 갯수가 n이상인 게시글 조회
	@Override
	public List<Board> selectBoardReplyCount(int n) {
		try {

			// 1. 전체 게시글을 가져옴.
			FindIterable<Document> list = this.boardColl.find();

			// 2. 반복한다
			List<Board> retList = new ArrayList<Board>();
			for (Document doc : list) {
				// 3. 게시글 번호를 이용해서 답글의 개수를 구한다.
				Bson filter = Filters.eq("board", doc.getLong("_id"));

				long replyCount = this.replies.countDocuments(filter);
				if (replyCount >= n) { // 답글의 개수가 전달받는 n보다 크다면
					Board board = new Board(); // board 객체를 만든다.
					board.setBrdNo(doc.getLong("_id"));
					board.setBrdTitle(doc.getString("title"));
					board.setBrdContent(doc.getString("content"));
					board.setBrdWriter(doc.getString("writer"));
					board.setBrdHit(doc.getLong("hit"));
					board.setBrdDate(doc.getDate("date"));
					retList.add(board); // 리스트에 추가한다.
				}
			}
			return retList; // if에 필터된 항목으로 구성된 retList를 반환한다.
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

}

//	// 출력문
//	public void printInfo(Bson filter) {
//		MongoCursor<Document> list = this.boardColl.find(filter).iterator();
//		while (list.hasNext()) {
//			Document doc = list.next();
//			System.out.println("글번호 : " + doc.getString("_id"));
//			System.out.println("제목 : " + doc.getString("title"));
//			System.out.println("내용 : " + doc.getString("content"));
//			System.out.println("글쓴이 : " + doc.getString("writer"));
//			System.out.println("------------------------------------");

각각의 메소드들을 구분해주기 위하여 '/* */' 주석을 이용하여 구분선을 그어두었다.

(한결 잘 보인다.)

참고로 본 implements 클래스를 구동하기 위한 기능용 클래스는 6일차에 제작했던 "MemberDB" 클래스를 이용하였다.

다음으로는 메인 클래스이다.

package day7;

import java.util.List;

public class BoardMain {

	public static void main(String[] args) {
		// 설계도면 obj = new 구현한클래스의생성자();
		BoardDAOImpl obj = new BoardDAOImpl();

		/*--------- 글번호와 제목 내용 작성자 전송하면 해당 글번호의 제목 내용 작성자를 변경하기-------*/

//		Board board = new Board();
//		board.setBrdNo(1);
//		board.setBrdTitle("새로운 제목");
//		board.setBrdContent("새로운 내용");
//		board.setBrdWriter("새 작성자");
//	    int ret = obj.updateBoard(board);
//		System.out.println(ret);
//		
		/*----------------------- 삭제하기 --------------------------------*/

//		Board board = obj.deleteBoard(100);
//      System.out.println(board.toString());

		/*-----------------------hit 이하의 게시물만 조회-----------------------*/

		// 목록으로 전송됨. 목록은 반드시 반복문을 통해서 1개씩 출력해야 함.
//		long num = 10;
//
//		List<Board> list = obj.selectBoardHitList(num);
//
//		if (list.isEmpty()) {  // list가 현재 아무것도 지정되지 않은 ArrayList() 이기 때문에 .isEmpty 라는  
//			System.out.println(num + "이하의 값이 없음.");
//		} else {
//			for (Board one : list) {
//				System.out.println(one.getBrdNo() + ", " + one.getBrdTitle() + ", " + one.getBrdHit() + ", "
//						+ one.getBrdWriter() + ", " + one.getBrdDate());
//			}
//		}

		/*-------------------------전체글 조회--------------------------*/

//		List<Board> list = obj.selectBoardList();
//		// list의 값을 1개씩 꺼내서 one 변수 보관하는 방식
//		for (Board one : list) {
//			System.out.println(one.getBrdNo() + ", " + one.getBrdTitle() + ", " + one.getBrdHit() + ", "
//					+ one.getBrdWriter() + ", " + one.getBrdDate());
//
//		}
//		System.out.println();
//
//		// 배열의 위치를 이용하여 출력하는 방식 결과값은 위와 동일함.
//		for (int i = 0; i < list.size(); i++) {
//			Board one = list.get(i);
//			System.out.println(one.getBrdNo() + ", " + one.getBrdTitle() + ", " + one.getBrdHit() + ", "
//					+ one.getBrdWriter() + ", " + one.getBrdDate());
//		}
//	}

		/*------------------ 보드 선택하기 -------------------------*/

		// 숫자를 입력하면 값에 해당하는 글을 출력

//		System.out.println(obj.selectBoardOne(2));

		/*------------------글제목, 내용, 작성자 정보만 전달됨.-------------------*/

		// 고객이 입력한 내용에 해당
//	    
//		Board board = new Board();
//		board.setBrdTitle("제목1");
//		board.setBrdContent("내용1");
//		board.setBrdWriter("작성자1");
//		System.out.println(obj.insertBoard(board));

		/*--------------------------------------------------------------------*/
	}
}

기능을 구분하고 각각 하나씩 구분해서 실행시켜본다고 주석처리를 해뒀다..!

실행결과

등록과 수정, 삭제 모두 잘 이루어진것을 확인하였다.


금일 배웠던 부분은 사실 실무에 들어가면 좀 더 간단한 형식으로 처리가 가능한 부분들이나, 현재 우리는 아직 개발을 배운지 얼마되지않은 햇병아리들(?) 이기 때문에 어떤식으로 구동이 이루어지는지에 대해서 자세하게 알기 위하여

세분화한 코드를 기술하였다.


profile
시작은 미약하지만, 그 끝은 창대하리라

0개의 댓글