컴퓨터는 잘못이 없다..

[JAVA]매개변수가 클래스타입인 메소드 호출해보기(+instanceof연산자, NullPointerException방지하는 법) 본문

공부/JAVA

[JAVA]매개변수가 클래스타입인 메소드 호출해보기(+instanceof연산자, NullPointerException방지하는 법)

도토리까꿍v 2020. 12. 28. 15:06
Contents 접기

[핵심]

1. 매개변수가 클래스타입인 메소드를 호출해보자.

2. instanceof 연산자에 대해 알아보자.

3. NullPointerException이 발생하는 경우와 방지하는 방법을 찾아보자 

 

 

[예제코드]

Phone.java

package test.mypac;
//extends는 어떤 클래스를 상속 받을 때 사용하는 예약어이다.
//어떤 클래스도 extends 하지 않으면 자동으로 Object 클래스를 상속받게 된다.
//따라서 Ojbect 클래스를 상속받을 거라면 생략이 가능하다.
public class Phone{
	
	//디폴트 생성자
	public Phone() {
		System.out.println("Phone 생성자 호출됨");
	}
	
	//전화거는 non-static 메소드
	public void call() {
		System.out.println("전화를 걸어요!");
	}
	
}

 

HandPhone.java

package test.mypac;

public class HandPhone extends Phone{
	
	//디폴트 생성자
	public HandPhone() {
		System.out.println("HandPhone() 생성자 호출됨");
	}

	//이동중에 전화를 걸어요
	public void mobileCall() {
		System.out.println("이동중에 전화를 걸어요");
	}
	
	//사진을 찍어요
	public void takePicture() {
		System.out.println("30만 화소의 사진을 찍어요!");
	}
}

 

SmartPhone.java

package test.mypac;

//SmartPhone 클래스를 종단 클래스로 만드는 final 얘약어 --> fianl은 고자를 만들어버린다.
public final class SmartPhone extends HandPhone{
	
	//디폴트 생성자
	public SmartPhone() {
		System.out.println("SmartPhone()생성자 호출됨");
	}

	//SmartPhone이 정의한 메소드 
	public void doInternet() {
		System.out.println("인터넷을 해요");
	}
	
	//SmartPhone메소드에서 부모클래스와 자기자신클래스 메소드 호출하기
	public void callMethod() {
		//SmartPhone클래스엔 call()함수가 없으니 부모의 call()함수가 호출된다.
		call(); 
		//SmartPhone 클래스엔 mobileCall()함수가 없으니 부모의 mobileCall()함수가 호출된다. 
		mobileCall(); 
		//SmartPhone클래스엔 takePicture()함수가 있으므로(재정의함) 
		//부모클래스의 takePicture()를 호출하고 싶을땐 super예약어를 이용한다.
		super.takePicture(); 
		//부모클래스의 takePicture()가 아닌 
		//SmartPhone클래스의 takePicture을 호출하고 싶을 땐 this 예약어를 사용한다.
		this.takePicture(); 
		//위에 1줄에서 this는 생략해도 된다. 
		//이경우 부모클래스에도, 자식클래스에도 takePicture이 존재하므로 자식의 takePicture가 호출된다.
		takePicture(); 
		
	}
	
	//HandPhone으로 부터 메소드 상속을 받긴 하겠지만
	//어떤 메소드는 내가 재정의를 하겠다!
	//이 메소드는 재정의한 메소드라고 표시해주는 어노테이션(@)
	//특별한 기능을 하는 것은 아니고 단지 재정의한 메소드라고 표시해주는 기능만 있다.
	@Override
	public void takePicture() {
		/*
		super 는 부모 객체를 가리키는 예약어이다.
		피 오버라이드된 부모 메소드도 만일 호출하려면 아래와 같이 호출하면 된다.
		super.takePicture();
		부모메소드를 호출해야하는지 아닌지는 그때 그때 클래스에 따라 다르므로
		클래스 사용법을 학습을 해서 선택을 해야한다. 
		*/
		super.takePicture();//부모 메소드를 호출해준다. super은 부모 클래스의 참조값을 가리킨다. 
		System.out.println("1000만 화소의 사진을 찍어요");
	}
		
}

 

MainClass06.java

package test.main;

import test.mypac.HandPhone;
import test.mypac.Phone;
import test.mypac.SmartPhone;

public class MainClass06 {
	
	public static void main(String[] args) {
		
		
		//usePhone 메소드를 호출하는 코드를 아래에 작성해보세요.
		MainClass06.usePhone(new Phone());
		
		//클래스명 MainClass06 생략가능 
		System.out.println("---------");
		usePhone(new HandPhone()); 
		System.out.println("---------");
		usePhone(new SmartPhone()); 
	
		
	}
	
	//Phone type을 인자로 전달받는 static 메소드
	public static void usePhone(Phone p) {
		//인자로 전달되는 참조값을 이용해서 메소드 호출하기
		p.call();
	}

}

 

MainClass06.java 실행결과

Phone 생성자 호출됨
전화를 걸어요!
---------
Phone 생성자 호출됨
HandPhone() 생성자 호출됨
전화를 걸어요!
---------
Phone 생성자 호출됨
HandPhone() 생성자 호출됨
SmartPhone()생성자 호출됨
전화를 걸어요!

 

 

MainClass07.java

package test.main;

import test.mypac.*;

public class MainClass07 {
	public static void main(String[] args) {
		MainClass07.usePhone(new Phone());
		System.out.println("-----");
		MainClass07.usePhone(new HandPhone());  
		System.out.println("-----");
		MainClass07.usePhone(new SmartPhone()); 
		
		//실행결과?
		//전화를 걸어요!
		//이동중에 전화를 걸어요
		//이동중에 전화를 걸어요
	}
	
	public static void usePhone(Phone p) {
		//인자가 new Phone()이라면  Phone p = new Phone();
		//인자가 new HandPhone()이라면 Phone p = new HandPhone();
		//인자가 new SmartPhone()이라면 Phone p = new SmartPhone();
		
		
		//A instanceof B
		//A: 객체, B : 클래스 혹은 인터페이스를 써야하고 A와 B가 상속관계여여한다.  
		//B가 A의 부모클래스이거나 자기자신클래스이면 true를 봔환하고 그렇지 않으면 false를 반환한다. 
		if(p instanceof HandPhone) { //instanceof 라는 연산자는 true or false로 리턴해준다.
			//p는 HandPhone타입 p2보다 부모타입인 Phone타입 변수이기 때문에 casting 해주어야한다.
			HandPhone p2 = (HandPhone)p; 
			p2.mobileCall();
		}else {
			p.call();
		}
	}

}

 

MainClass07.java 실행결과

Phone 생성자 호출됨
전화를 걸어요!
-----
Phone 생성자 호출됨
HandPhone() 생성자 호출됨
이동중에 전화를 걸어요
-----
Phone 생성자 호출됨
HandPhone() 생성자 호출됨
SmartPhone()생성자 호출됨
이동중에 전화를 걸어요

 

MainClass08.java

package test.main;

import test.mypac.*;

public class MainClass08 {
	public static void main(String[] args) {
		System.out.println("메인 메소드가 시작되었습니다.");
		Phone p1 = null;
		Phone p2 = new Phone();
		
		MainClass08.usePhone(p1);
		MainClass08.usePhone(p2);
		
		System.out.println("메인메소드가 종료됩니다.");
		
	}
	
	public static void usePhone(Phone p) {
		//p가 null이면 NullPointerException 에러가 난다. 
		p.call();
		
		//여기에서 NullPointerException을 방지하는 방법이 있을까요?
		//if(p != null) {
		//	p.call();
		//}
	}

}

 

MainClass08.java 실행결과

메인 메소드가 시작되었습니다.
Exception in thread "main" java.lang.NullPointerExceptionPhone 생성자 호출됨

	at test.main.MainClass08.usePhone(MainClass08.java:20)
	at test.main.MainClass08.main(MainClass08.java:11)

 

 

 

[코드설명]

 

1. static메소드에서는 static자원만 사용가능하다.

 

┌MainClass06.java의 usePhone() 메소드 

 

 

2. 매개변수가 클래스타입인 함수를 호출하면 이런 형태가 된다.

 

MainClass06.java 의 MainClass06.usePhone(new Phone()); 호출 

 

MainClass06.java 의 MainClass06.usePhone(new HandPhone()); 호출 

 

MainClass06.java 의 MainClass06.usePhone(new SmartPhone()); 호출 

 

 

 

3. MainClass06.java 실행결과를 분석해보자

 

MainClass06.java의 실행결과 확인

 

 

4. instanceof 연산자를 이용해서 넘기는인자에 따라 다른 결과를 낼 수 있도록 만들어보자 

* A instanceof B 알아두기!!

A : 객체, B: 클래스 혹은 인터페이스 ( A과 B는 상속관계여야한다.)

A의 클래스가 B클래스와 같거나 B클래스가 부모클래스라면 라면 true를 반환하고 그게 아니라면 false를 반환한다.

 

MainClass07.java

▲Phone객체의 참조값을 usePhone메소드의 인자로 넘기면

if(p instanceof HandPhone)에서

Phone 객체는 HandPhone클래스이거나 HandPhone클래스의 자식 클래스이니? 라고 물어본다. --> false를 반환

따라서 else문의 p.call()을 호출한다.

▲HandPhone객체의 참조값을 usePhone메소드의 인자로 넘기면

if(p instanceof HandPhone)에서

HandPhone객체는 HandPhone클래스이거나 HandPhone클래스의 자식 클래스이니? 라고 물어본다. --> true를 반환 

따라서 if문의 p2.mobileCall()을 호출한다. 

▲SmartPhone객체의 참조값을 usePhone메소드의 인자로 넘기면

if(p instanceof SmartPhone)에서

SmartPhone객체는 HandPhone클래스이거나 HandPhone클래스의 자식 클래스이니? 라고 물어본다. --> true를 반환 

따라서 if문의 p2.mobileCall()을 호출한다. 

 

 

MainClass07.java의 usePhone메소드에서 다운캐스팅 과정이 필요한 이유 

 

 

5. NullPointerException발생과 해결방법

 

MainClass08.java에서 NullPointerException발생하는 경우

▲null을 usePhone메소드의 인자로 넘겨주면 p.call() 했을 때 NullPointerException이 발생하는 것을 확인할 수 있다. 

 

 

 

MainClass08.java 의 NullPointerException 방지하는 법!

null체크를 해주기 전 실행결과

null체크를 해준 후 실행결과

 

Comments