[Java의 정석] 객체지향개념 2 (OOP 2)

Shiba·2023년 6월 2일
post-thumbnail

📓 상속과 다형성 (Inheritance and Polymorphism)

📕 상속 (Inheritance)

📃 정의

  • 기존의 클래스재사용해서 새로운 클래스를 작성하는 것
  • 두 클래스를 조상과 자손으로 관계를 맺어주는 것
  • 자손조상의 모든 멤버를 상속받는다 (생성자, 초기화블럭 제외)
  • 자손의 멤버개수는 조상보다 적을 수 없다 (같거나 많다)
class 자손클래스 extends 조상클래스 {
	//내용
}
class Point{
	int x;
    int y;
}

//상속을 사용하지 않을때
class Point3D{
	int x;
    int y;
    int z;
}

//상속 사용
class Point3D extends Point{
	int z;
}

//두 클래스 모두 x,y,z를 변수로가지고 있음.

📃 클래스간의 관계

🔷 상속관계(Inheritance)

  • 공통부분조상에서 관리, 개별부분자손에서 관리
  • 조상의 변경자손에 영향, 자손의 변경은 조상에 영향이 없다
class Parent {} // 조상

class Child extends Parent {} //자손
class Child extends Parent {} //자손
class GrandChild extends Child {} //자손의 자손

🔷 포함관계(Composite)

  • 클래스의 멤버변수로 다른 클래스를 선언하는 것
  • 작은 단위의 클래스를 먼저 만들고, 이 둘을 조합해서 하나의 커다란 클래스를 만든다.
//포함관계
class Car {
	// 객체가 클래스에 포함!
	Engine e = new Engine(); 
    Door[] d = new Door[4];
}

🔷 관계결정하기 상속 vs 포함

  • 상속관계is-a 이다.
    ex) 원은 점이다.(Circle is a Point.)

  • 포함관계has-a 이다.
    ex) 원은 점을 가지고 있다.(Circle has a Point.)
//상속관계 - is-a
class Circle extends Point {
	int r;
}

//포함관계 has-a
class Circle {
	Point c = new Point();
    int r;
}

📃 단일 상속 (Single Inheritance)

  • Java는 단일 상속만을 허용한다. (C++은 다중 상속 허용)
class Unit {
	void move() {};
}

class Machine {
	void move() {};
}


class Tank extends Unit, Machine { //다중상속을 허용하지 않는다.
	//메소드간 충돌 우려 - move()호출시 어떤 클래스의 move()가 호출될지 모름
}
  • 비중이 높은 클래스 하나만 상속관계, 나머지포함관계로 한다.
class TV {
	boolean power;
    int channel;
    
    void power() { power = !power; }
    void channelUp() { ++channel; }
    void channelDown() { --channel; }
}


class VCR {
	boolean power;
    int counter = 0;
    
    void power() { power = !power }
    void play() { //... }
    void stop() { //... }
}

//비중이 큰 TV클래스를 상속함 - TV와 상속관계를 가짐.
class TVCR extends TV {
	VCR vcr = new VCR(); //VCR과 포함관계를 가짐.
    int counter = vcr.counter;
    
    void play() {
    	vcr.play();
    }
    
    void stop() {
    	vcr.stop();
    }
}

📃 Object클래스

  • 조상이 없는 클래스자동적으로 Object클래스를 상속받음.
  • 상속계층도의 최상위에는 Object클래스가 위치한다.
  • 모든 클래스는 Object클래스에 정의된 11개의 메소드를 상속받는다.
    • toString(), equals(Object obj), hashCode(), ...

class TV 'extends Object' { //생략되어있음
	//...
    System.out.println(t);
    System.out.println(t.toString());
    //두 출력의 결과는 같다! t는 t.toString()과 같다.
}

class CaptionTV extends TV {
	//...
}

🔸 제어자 (Modifiers)

📃 제어자란?

  • 클래스, 변수, 메소드의 선언부에 사용. 부가적인 의미 부여.
  • 크게 접근 제어자그 외의 제어자로 구분.
  • 하나의 대상여러 개의 제어자를 조합해서 사용가능.
    하지만 접근 제어자단 하나만 사용 가능.
접근 제어자그 외의 제어자
public, protected, default, privatestatic, final, abstract, native, synchronized, translent, volatile, strictfp

📃 접근 제어자 (Access Modifier)

🔷 접근제어자란?

멤버 또는 클래스에 사용되어, 외부로부터의 접근을 제한한다.

🔷 사용될수 있는 곳

클래스, 멤버변수, 메소드, 생성자

🔷 종류와 범위

  • private : 같은 클래스 내에서만 접근이 가능
  • default : 같은 패키지 내에서만 접근이 가능
  • protected : 같은 패키지 내, 다른 패키지자손클래스에서 접근이 가능
  • public : 접근 제한이 없음

🔷 접근제어자를 이용한 캡슐화

  • 외부로부터 데이터를 보호
  • 외부에는 불필요하거나 내부적으로 사용되는 부분을 감춤
class Time {
	private int hour;
    private int minute;
    private int second;
    
    Time(int hour, int minute, int second){
    	setHour(hour);
        setMinute(minute);
        setSecond(second);
    }
    
    public int getHour() { return hour; }
    
    public void setHour(int hour) {
    	if(hour < 0 || hour > 23) return;
        this.hour = hour;
    }
    
    public String toString() {
    	return hour + ":" + minute + ":" + second;
    }
}

public static void main(String[] args) {
	Time t = new Time(12, 35, 30);
    System.out.println(t); // t는 t.toString()과 같다!
    
    //t.hour = 13; //에러!! private는 직접접근불가
    t.sethour(t.getHour() + 1); //set메소드로 간접접근만 가능
    
    System.out.println(t);
   
}
// 12:35:30
// 13:35:30 출력

🔷 생성자의 접근제어자

  • 일반적으로 생성자의 접근제어자클래스의 접근제어자일치
  • 생성자에 접근제어자를 사용함으로써 인스턴스 생성 제한
final class Singleton {
	private static Singleton s = new Singleton();
    
    private Singleton() { // 생성자
    	//...
    }
    
    public static Singleton getInstance() {
    	if(s==null){
        	s = new Singleton();
        }
        return s;
    }
}

class SingletonTest {
    public static void main(String[] args) {
    	// Singleton s = new Singleton(); //에러!! 생성자에 접근불가
        Singleton s1 = Singleton.getInstance(); //생성자에 간접접근
    }
}

📃 그 외의 제어자

🔷 static

클래스의, 공통적인

◼ 사용될 수 있는 곳

멤버변수, 메소드, 초기화 블럭

class StaticTest {
	static int width = 200;
    static int height = 100;
    
    static { // 클래스 초기화 블럭
    	// static변수의 복잡한 초기화 수행.
    }
    
    static int max(int a, int b){
    	return a> b ? a : b;
    }
}

🔷 final

마지막의, 변경될 수 없는

◼ 사용될 수 있는 곳

클래스, 메소드, 멤버변수, 지역변수

업로드중..

final class FinalTest {
	final int MAX_SIZE = 10; //멤버변수
    
    final void getMaxSize() {
    	final LV = MAX_SIZE; //지역변수
        return MAX_SIZE;
    }
}

class Child extends FinalTest {
	void getMaxSize() {} // 오버라이딩으로 재정의 불가
}

◼ 생성자를 통한 final 멤버변수 초기화

final이 붙은 변수는 상수이므로 보통은 선언과 초기화를 동시에 하지만, 인스턴스변수의 경우 생성자에서 초기화할 수 있다.

class Card {
	final int NUMBER; // 상수지만 선언과 함께 초기화 하지 않고
    final String KIND; // 생성자에서 단 한번만 초기화할 수 있다.
    static int width = 100;
    static int height = 250;
    
    Card(String kind, int num) {
    	KIND = kind;
        NUMBER = num;
    }
    
    Card() {
    	this("HEART", 1);
    }
    
    public String toString() {
    	return "" + KIND + " " + NUMBER;
    }
}

🔷 abstract

추상의, 미완성의

◼ 사용될 수 있는 곳

클래스, 메소드

업로드중..

abstract class AbstractTest { // 추상클래스
	abstract void move(); // 추상메소드
}

🔸 제어자의 조합

  1. 메소드에 static과 abstract를 함께 사용할 수 없다.
    • static메소드구현부가 있는 메소드에만 사용가능

  2. 클래스에 abstract와 final동시사용할 수 없다.
    • final은 확장불가, abstract는 확장하여 완성시켜야함. 서로 상반됨.

  3. abstract메소드의 접근제어자가 private일 수 없다.
    • abstract메소드는 자손클래스에서 구현해야함. private는 접근불가.

  4. 메소드에 private와 final같이 사용할 필요는 없다.
    • private메소드도 오버라이딩이 불가. 둘중 하나만 써도 충분.

📘 오버라이딩(Overriding)

📃 정의

  • 조상클래스로부터 상속받은 메소드의 내용상속받는 클래스에 맞게 변경하는 것
class Point{
	int x;
    int y;
    
    String getLocation() {
    	return "x :" + x + "y :" + y;
    }
}

class Point3D extends Point{
	int z;
    String getLocation() { //오버라이딩!
    	return "x :" + x + "y :" + y + "z :" + z;
    }
}

📃 조건

  1. 선언부같아야 한다. (이름, 매개변수, 리턴타입)
  2. 접근제어자좁은 범위로 변경할 수 없다.
    • 조상의 메소드가 protected라면 범위가 같거나 넓은 protected, public만 사용 가능.
  3. 조상클래스의 메소드보다 많은 수의 예외를 선언할 수 없다.

📃 오버로딩 vs 오버라이딩

  • 오버로딩(overloading) - 기존에 없는 새로운 메소드를 정의하는 것

  • 오버라이딩(overriding) - 상속받은 메소드의 내용을 변경하는 것
class Parent {
	void parentMethod() {}
}


class Child extends Parent {
	void parentMethod() {} //오버라이딩
    void parentMethod(int i) {} //오버로딩
    
    void childMethod() {} //자식이 메소드 선언
    void childMethod(int i) {} //오버로딩
    void childMethod() {} //에러!! 중복정의된 메소드
}

📘 super와 super()

📃 참조변수 super

📜 this : 인스턴스 자신을 가리키는 참조변수. 인스턴스의 주소가 저장되어있음. 모든 인스턴스 메소드에 지역변수로 숨겨진 채 존재.

  • super : this와 같음. 조상의 멤버와 자신의 멤버를 구별하는데 사용.
class Point{
	int x;
    int y;
    
    String getLocation() {
    	return "x :" + x + "y :" + y;
    }
}

class Point3D extends Point{
	int z;
    String getLocation() { //오버라이딩!
    	//return "x :" + x + "y :" + y + "z :" + z;
        return super.getlocation() + "z :" + z; //조상 메소드 호출
    }
}

📃 생성자 super()

  • 자손클래스의 인스턴스 생성시 조상의 멤버들도 초기화가 필요.
    따라서 첫 문장에서 조상의 생성자호출해야한다.

Object클래스를 제외모든 클래스의 생성자 첫 줄에는 생성자를 호출!
그렇지 않으면 컴파일러가 자동적으로 'super();'를 생성자의 첫 줄에 삽입!

class Point {
  int x;	
  int y;
  
  Point() {
  	this(0,0);
  }
  
  Point(int x, int y){
  	'super();' // Object();와 같음. 컴파일러가 자동으로 삽입
  	this.x = x;
  	this.y = y;
  }
  
}
  
class Circle extends Point{
    int z;
  
    Circle(int x, int y, int z){
  		'super();' // Point();와 같음. 컴파일러가 자동으로 삽입
  		this.x = x;
  		this.y = y;
  		this.z = z;
    }
}  

❗ 주의해야할 오류!

class Point {
  int x;	
  int y;
  
  Point(int x, int y){
  	'super();' // Object();와 같음. 컴파일러가 자동으로 삽입
  	this.x = x;
  	this.y = y;
  }
  
}
  
class Circle extends Point{
    int z;
  
    Circle(int x, int y, int z){
  		'super();' // Point();와 같음. 컴파일러가 자동으로 삽입
  		this.x = x;
  		this.y = y;
  		this.z = z;
    }
}  
  
- 에러!! Point()가 없으므로 조상멤버를 초기화할 수 없다고 판단해버림!!

📙 다형성 (Polymorphism)

📃 다형성이란?

조상타입의 참조변수자손타입의 객체를 다룰 수 있는 것

📃 다형성 사용법

🔷 조상 → 자손

class TV 'extends Object' { //생략되어있음
	boolean power;
    int channel;
    
    void power() {}
    void channelUP { ++channel; }
    void channelDown { --channel; }
}

class CaptionTV extends TV {
	String text;
    void caption() {}
}

TV t = new TV;
CaptionTV = new CaptionTV();

//다음과 같이 사용가능
TV t = new CaptionTV(); 
// CaptionTV가 물려받은 변수와 메소드는 사용가능!
// CaptionTV가 가진 변수와 메소드는 사용불가!

- 버튼이 5개인 리모컨으로 기능이 7개인 TV를 조작해도 조작에 에러가 발생하진 않음

반대로 자손타입의 참조변수조상타입의 인스턴스를 참조할 수는 없다!!

CaptionTV ct = new TV(); //에러!! 자손타입으로 조상타입을 참조할 수 없음

- 버튼이 7개인 리모컨으로 기능이 5개인 TV를 조작시 두개의 버튼은 에러발생

업로드중..

🔷 참조변수의 형변환

  • 사용할 수 있는 멤버의 갯수를 조절하는 것
  • 서로 상속관계에 있는 타입간의 형변환만 가능
  • 자손타입에서 조상타입으로 형변환하는 경우, 형변환 생략가능
    • 자손타입 → 조상타입 (Up-casting) : 형변환 생략가능
    • 자손타입 ← 조상타입 (Down-casting) : 형변환 생략불가
class Car { //조상
	String color;
    int door;
    
    void drive() {}
    void stop() {}
}

class FireEngine extends Car { //자식1
	void water() {}
}

class Ambulance extends Car { //자식2
	void siren() {}
}

public static void main(String[] args){
	Car car = null;
    FireEngine fe = new FireEngine();
    FireEngine fe2 = null;
    
    fe.water();
    car = fe; // car = (Car)fe; 조상 <- 자손. 형변환 생략가능
    //car.water(); //에러!! Car타입은 water()메소드를 사용하지못함
    fe2 = (FireEngine)car; // 조상 -> 자손. 형변환 생략불가
    fe2.water(); //FireEngine 타입이므로 사용가능!
    
    //Ambulance a = (Ambulance)f;//에러! 상속관계가 아닌 클래스간 형변환 불가
}

❗ 객체 과 변수의 주소값은 모두 그대로! 단지 타입이 바뀔뿐임!!!
(TV의 상태가 바뀌는게 아닌 리모컨이 바뀌는 것)

🔷 instanceof 연산자

참조변수 형변환 가능여부 확인에 사용. 가능하면 true 반환

◼ 예제

FireEngine fe = new FireEngine();

System.out.println(fe instanceof Object); //Object는 최고조상이므로 true
System.out.println(fe instanceof Car); //Car은 조상이므로 true
System.out.println(fe instanceof FireEngine); //자기자신이므로 true

- 조상으로 형변환이 가능!

📃 다형성의 장점

🔷 매개변수의 다형성

참조형 매개변수는 메소드 호출시, 자신과 같은 타입 또는 자손타입의 인스턴스넘겨줄 수 있다.

class Product{
	int price;
    int bonusPoint;
}

class TV extends Product {}
class Computer extends Product {}
class Audio extends Product {}

class Buyer {
	int money = 1000;
    int bonusPoint = 0;
	
    void buy(TV t) { //TV만 살수 있음. 타입이 다르기 때문
		money = t.price;
    	bonusPoint += t.bonusPoint;
	}
	- 다른물건도 사려면 같은 꼴의 메소드를 물건 갯수만큼 만들어야함!

	//다형성사용. 다음과같이 메인함수에 객체생성
	Product p1 = new TV(); 
	Product p2 = new Computer();
	Product p3 = new Audio();
	//
    
	void buy(Product p) { //모두 Product타입이므로 사용가능!
		money = p.price;
    	bonusPoint += p.bonusPoint;
	}
	-이 메소드 하나만으로 모든 물건을 살수 있게됨!
}

🔷 여러 종류의 객체를 배열로 다루기

조상타입의 배열자손들의 객체를 담을 수 있다.

Product p1 = new TV(); 
Product p2 = new Computer();
Product p3 = new Audio();

//배열 사용
Product[] p = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();

📘 추상 클래스 (Abstract Class)

📃 추상 클래스란?

미완성 설계도. 미완성 메소드갖고 있는 클래스

abstract class Player {
	abstract void play(int pos); //{}이 없는 미완성메소드(추상메소드)
    abstract void stop(); //추상메소드
}
  • 추상클래스의 인스턴스는 생성불가
    • 상속을 통해 추상메소드를 완성해야 인스턴스 생성가능
Player p = new Player(); // 에러! 

class AudioPlayer extends Player {
	void play(int pos) {}
    void stop() {}
}

AudioPlayer ap = new AudioPlayer(); //생성가능!
Player p = new AudioPlayer(); //다형성도 가능!!

💡 추상메소드(Abstract Method)

미완성 메소드. 구현부({ })가 없는 메소드

abstract 리턴타입 메소드이름(); // 추상메소드 작성
  • 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우 사용.

추상메소드가 있으면 추상클래스이다!

📃 추상 클래스의 작성

공통적으로 사용될 수 있는, 사용되는 부분을 추상클래스로 만든다.

class Marine {
	int x,y; // 공통부분
    void move(int x, int y) {} // 공통부분
    void stop() {}; // 공통부분
    void stimpack() {};
}

class Tank {
	int x,y; // 공통부분
    void move(int x, int y) {} // 공통부분
    void stop() {}; // 공통부분
    void changeMode() {};
}

class Dropship {
	int x,y; // 공통부분
    void move(int x, int y) {} // 공통부분
    void stop() {}; // 공통부분
    void load() {};
    void unLoad() {};
}

//추상클래스 사용
abstract class Unit {
	int x,y; // 공통부분
    abstract void move(int x, int y); // 공통부분-각각작성
    void stop() {}; // 공통부분
}

class Marine {
    void move(int x, int y) {} 
    void stimpack() {};
}

class Tank {
	int x,y; // 공통부분
    void move(int x, int y) {} 
    void changeMode() {};
}

class Dropship {
    void move(int x, int y) {} // 공통부분
    void load() {};
    void unLoad() {};
}

-코드가 간결해지고, 다형성도 사용이 가능해짐!

Unit[] group = new Unit[3]; 
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Dropship();
//Unit[] group = {new Marine(), new Tank(), new Dropship()}; 과 같음

//다중 이동 가능!
for(int  i = 0; i<group.length; i++){
	group[i].move(100,200); //각각의 move()메소드가 호출된다
}

📃 사용이유

코드의 관리가 용이. - 중복제거, 변경에 유리한 코드가 만들어짐

업로드중..

📘 인터페이스 (Interface)

📃 인터페이스란?

추상메소드의 집합 - 프로그래밍 관점

🔸 추상클래스와 인터페이스의 차이

추상클래스 : 일반클래스(멤버변수, 메소드) + 추상메소드
인터페이스 : only 추상메소드 - iv를 가질 수 없다.

📃 인터페이스 선언

//모든멤버가 public
interface 인터페이스이름 {
	public static final 타입 상수이름 =;
    public abstract 메소드이름(매개변수);
}

//예제
interface PlayingCard {
	//모든 변수는 상수 - public static final 생략가능
	public static final int SPADE = 4;
    final int DIAMOND = 3; //public static final int DIAMOND = 3;
    static int HEART = 2; //public static final int HEART = 2;
    int CLOVER = 1; //public static final int CLOVER = 1;
    
    //모든 메소드는 추상메소드 - public abstract 생략가능
    public abstract String getCardNumber();
    String getKind(); // public abstract String getKind();
}

📃 인터페이스의 상속

  • 인터페이스의 조상인터페이스만 가능(Object가 최고 조상이 아님)
  • 다중 상속이 가능.(추상메소드는 충돌문제가 없음)
interface Movable {
	void move(int x, int y);
}

interface Attackable {
	void attack(Unit u);
}

interface Fightable extends Movable, Attackable {}

📃 인터페이스의 구현

인터페이스에 정의된 모든 추상메소드를 완성하는 것

class 클래스이름 implements 인터페이스이름 {
	//인터페이스에 정의된 '모든 추상메소드'를 구현
}

//예제
class Fighter implements Fightable {
	void move(int x, int y) { //이동 }
    void attack(Unit u) { //공격 }
}

일부만 구현하는 경우, 클래스앞에 abstract를 붙여야한다

abstract class Fighter implements Fightable {
	void move(int x, int y) { //이동 }
}

📃 인터페이스의 다형성

🔷 인터페이스 = 조상

인터페이스구현클래스의 조상으로 취급한다.

class Fighter extends Unit implements Fightable {
	public void move(int x, int y) {}
    public void attack(Fightable f) {}
}

Unit u = new Fighter(); //조상이 자손을 참조
Fightable f = new Fighter(); //인터페이스도 가능

🔷 인터페이스의 매개변수 다형성

인터페이스 타입 매개변수인터페이스를 구현한 클래스의 객체만 가능

interface Fightable {
	void move(int x, int y);
    
    //Fightable 인터페이스를 구현한 클래스의 인스턴스만 가능
    void attack(Fightable f); 
}

🔷 인터페이스의 리턴타입

인터페이스메소드의 리턴타입으로 지정할 수 있다.

//인터페이스를 구현한 클래스의 인스턴스를 반환
Fightable method() {
	Fighter f = new Fighter(); //Fighter는 Fightable을 구현한 클래스
    return f;
    // 위 문장들을 return new Fighter(); 로 줄일 수 있음
}

📃 인터페이스의 장점

  • 두 대상(객체)간의 '연결, 대화, 소통'을 돕는 '중간역할'을 한다
  • 표준화가 가능하다. - JDBC를 통한 DB관련 메소드들을 표준화함.

선언(설계)과 구현을 분리시킬 수 있게 한다.

class B { 
	public void method() {
    	System.out.println("B");
    }
}

//분리

interface I {
	void method();
}

class B implements I {
	public void method() {
    	System.out.println("B");
    }
}

- 결합을 느슨하게하여 변경에 유리한 코드 작성이 가능

//직접적인 관계의 A와 B
class A { 
	public void methodA(B b) { //B를 C로 변경시 변경이 필요하다
    	b.method(); 
    }
}

class B { 
	public void method() {
    	System.out.println("B");
    }
}

class C { 
	public void method() {
    	System.out.println("C");
    }
}

//간접적인 관계의 A와 B
class A { 
	public void method(I i) { //B를 C로 바꾸어도 변경 불필요
    	i.method();
    }
}

class B implements I {
	public void method() {
    	System.out.println("B");
    }
}

class C implements I {
	public void method() {
    	System.out.println("C");
    }
}

서로 관계없는 클래스들의 관계를 맺어줄 수 있다.

class Tank extends GroundUnit{
	int x,y; 
    void move(int x, int y) {} 
    void changeMode() {};
}

class Dropship extends AirUnit{
    void move(int x, int y) {} 
    void load() {};
    void unLoad() {};
}
//두 클래스는 관계가 없다!

TankDropship은 repairable 가능하다.
void repair(Tank t) {}
void repair(Dropship d) {} 
- 함수의 오버로딩은 코드의 중복을 발생시킴.

//인터페이스 사용
interface Repairable {}

class Tank extends GroundUnit implements Repairable{}

class Dropship extends AirUnit implements Repairable{}


void repair(Repairable r) {} 

- GroundUnitAirUnitRepairable로 관계를 맺어줌.

🔸 디폴트 메소드와 static메소드

인터페이스디폴트 메소드, static메소드 추가 가능 (JDK1.8부터)

🔹 디폴트 메소드의 등장 배경

인터페이스에 새로운 메소드(추상메소드)추가하기 어려움.
- 해결책 => 디폴트 메소드(default method)

interface Repairable{
	//모든 자손이 똑같이 구현하는 추상메소드
    //이 메소드를 모든 인터페이스의 자손이 구현해야함. - 코드의 중복
	void EqualMethod(); 
}

class Tank extends GroundUnit implements Repairable{}

class Dropship extends AirUnit implements Repairable{}

-새로운 공통 메소드를 추가해야할때마다 모든 자손들이 구현을 해야함. - 번거로움

//
interface Repairable{
	//모든 자손이 똑같이 사용하는 인스턴스 메소드
    //자손이 구현하지 않고 인터페이스 자체에서 구현.
	default void EqualMethod() {}; //구현부가 존제
}

🔹 디폴트 메소드가 충돌할 때의 해결책

디폴트메소드기존의 다른 메소드와 충돌할 수 있음

  1. 여러 인터페이스의 디폴트 메소드 간의 충돌
    • 인터페이스를 구현한 클래스에서 디폴트 메소드를 오버라이딩!
interface Unit{
	default void move() {}; 
}

interface GroundUnit{
	default void move() {};
}

class Tank implements Unit,GroundUnit {}

Tank t = new Tank();
t.move(); //에러! move()가 충돌됨!



class Tank implements Unit,GroundUnit {
	void move() {} //오버라이딩
}

Tank t = new Tank();
t.move(); //OK
  1. 디폴트 메소드조상클래스의 메소드 간의 충돌
    • 조상클래스의 메소드가 상속되고, 디폴트 메소드는 무시
class Unit{
	void move() {}; 
}

interface GroundUnit{
	default void move() {};
}

class Tank extends Unit implements GroundUnit {}

Tank t = new Tank();
t.move(); //조상클래스의 move()가 실행됨!

📒 내부 클래스 (Inner Class), 익명클래스 (Anonymous Class)

📃 내부 클래스 (Inner Class)

🔹 내부 클래스란?

클래스 클래스

class A { //외부 클래스
	//...
    
    class B { //내부 클래스
    	//... 
    }
}

🔹 내부 클래스의 장점

  • 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
  • 코드의 복잡성줄일 수 있다.(캡슐화)
class A { //외부 클래스
	int i;
    int plus(int i) {};
    
    class B { //내부 클래스
    	int sum;
        sum = plus(i); //객체 생성없이 외부클래스 접근가능
    }
}

🔹 내부 클래스의 종류와 특징

내부 클래스의 종류와 범위는 변수와 동일

class Outer {
	class Inner {} // iv(인스턴스 변수)와 같음
    static class StaticInner {} // cv(클래스 변수)와 같음
    
    void outerMethod() {
    	class LocalInner {} // lv(지역 변수)와 같음
    }
}
내부 클래스특징
인스턴스 클래스
(instance class)
외부 클래스의 멤버변수 선언위치에 선언하며,
외부 클래스의 인스턴스멤버처럼 다루어진다.
주로 외부클래스의 인스턴스맴버들과 관련된 작업에 사용될 목적으로 선언
스태틱 클래스
(static class)
외부 클래스의 멤버변수 선언위치에 선언하며,
외부 클래스의 static멤버처럼 다루어진다.
주로 외부 클래스의 static멤버, 특히 static메소드에서 사용될 목적으로 선언
지역 클래스
(local class)
외부 클래스의 메소드나 초기화블럭 안에 선언하며,
선언된 영역 내부에서만 사용 가능
익명 클래스
(anonymous class)
클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스(일회용)

🔹 내부 클래스의 제어자와 접근성

내부 클래스의 제어자는 변수에 사용 가능한 제어자와 동일

class Outer {
	private class Inner {} //private iv(인스턴스 변수)와 같음
    protected static class StaticInner{}//protected cv(클래스 변수)와 같음
    
    void outerMethod() {
    	class LocalInner {} // lv(지역 변수)와 같음
    }
}

static 내부클래스만 static 멤버를 가질 수 있음

class Outer{
	class InstanceInner {
    	int iv = 100;
        static int cv = 100; //에러! 선언불가
        final static int CONST = 100; //상수는 허용
    }
    
    static class StaticInner { // 외부 클래스의 인스턴스 멤버에 접근 불가
    	int iv = 200;
        static int cv = 200; //static클래스만 static메소드를 가질 수 있다.
    } 
    
    void method() {
    	class LocalInner{
        	int iv = 200;
            static int cv = 200;//에러! 선언불가
            final static int CONST = 100; //상수는 허용
        }
    }
}

외부 클래스의 private멤버내부클래스에서 접근가능하다.

class Outer {
	private int outerIv = 0;
    static int outerCv = 0;
    
    class InstanceInner {
    	int iiv = outerIv; //외부 클래스의 private멤버 접근 가능
        int iiv2 = outerCv;
    }
    
    static class StaticInner {
    //static클래스는 외부클래스의 인스턴스멤버에 접근불가
    	int siv = outerIv; //에러! 
        static int scv = outerCv; //static에는 접근 가능
    }
    
    void method() {
    	int lv = 0;
        final int Lv = 0;
    	class LocalInner{
        	int liv = outerIv;
            int liv2 = outerCv;
            
            //외부 클래스의 지역변수는 final이 붙은 변수(상수)만 접근가능
            //내부클래스의 객체가 지역변수보다 더 오래 생존가능해서
            int liv3 = lv; //에러! 
            //(JDK1.8부터 에러아님-상수로 간주) lv가 값이 바뀐다면 에러!!
            int liv4 = Lv; //상수는 ok
        }
    }
}

🔹 내부 클래스에 접근하는 방법

외부 클래스의 인스턴스먼저 만든 뒤, 내부 클래스 인스턴스 생성

public static void main(String[] args) {
	Outer oc = new Outer(); //외부 클래스 인스턴스 생성
    Outer.InstanceInner li = oc.new InstanceInner(); //내부 클래스 인스턴스
    
    
    //static내부 클래스의 인스턴스는 외부클래스를 먼저 생성하지 않아도 됨.
    Outer.StaticInner si = new Outer.StaticInner();
}

🔹 외부 클래스와 내부 클래스의 변수 이름이 같을때

외부 클래스와 내부 클래스의 변수 이름이 같을때는 this 사용

class Outer{
	int value = 30; //Outer.this.value
	class Inner {
    	int value = 20; //this.value
        
        void method() {
        	int value = 10; //value
            System.out.println(value + this.value + Outer.this.value);
        }
    }
}

📃 익명클래스 (Anonymous Class)

🔹 익명 클래스란?

이름이 없는 일회용 클래스. 정의와 생성을 동시에

new 조상클래스이름() {
	//선언
}

new 인터페이스이름() {
	//선언
}

//Ex)

Object iv = new Object() {
	void method() {};
}

static Object cv = new Object() {
	void method() {};
}

void method() {
	Object lv = new Object() {
    	void lvMethod() {};
    }
}
profile
모르는 것 정리하기

0개의 댓글