static 메소드
원래는 인터페이스에 추상 메소드만 선언할 수 있는데 JDK1.8부터 default 메소드와 static 메소드도 추가할 수 있게 되었다. static 메소드는 인스턴스와 관계가 없는 독립적인 메소드이기 때문에 예전부터 인터페이스에 추가하지 못할 이유가 없었다.
그러나 자바를 보다 쉽게 배울 수 있도록 규칙을 단순화 해야 했으므로 인터페이스의 모든 메소드는 추상 메소드여야한다는 규칙에 예외를 두지 않았다. 덕분에 인터페이스와 관련된 static 메소드는 별도의 클래스에 따로 두어야 했다.
가장 대표적인 것으로 java.util.Collecion 인터페이스가 있는데, 이 인터페이스와 관련된 static 메소드들이 인터페이스에는 추상 메소드만 선언할 수 있다는 원칙 때문에 별도의 클래스, Collection이라는 클래스에 들어가게 되었다.
만일 인터페이스에 static 메소드를 추가할 수 있었다면, Collection 클래스는 존재하지 않을 것이다. 그리고 인터페이스의 static 메소드 역시 접근 제어자가 항상 public이며, 생략할 수 있다.
default 메소드
조상 클래스에 새로운 메소드를 추가하는 것은 별 일이 아니지만, 인터페이스의 경우에는 보통 큰 일이 아니다. 인터페이스에 메소드를 추가한다는 것은, 추상 메소드를 추가한다는 것이고, 이 인터페이스를 구현한 기존의 모든 클래스들이 새로 추가된 메소드를 구현해야하기 때문이다.
인터페이스가 변경되지 않으면 제일 좋겠지만 아무리 설계를 잘 하더라도 언젠가 변경을 해줘야 한다.
JDK의 설계자들은 고심 끝에 default 메소드라는 것을 고안해냈다. default메소드는 추상 메소드의 기본적인 구현을 제공하는 메소드로 추상 메소드가 아니기 때문에 default 메소드가 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.
default 메소드는 앞에 키워드 default를 붙이며, 추상 메소드와 달리 일반 메소드처럼 몸통{} 이 있어야 한다. 접근 제어자는 public이며, 생략 가능하다.
interface MyInterface
{
interface MyInterface
{
void method();
void method();
void newMethod(); // 추상 메소드 > default void newMethid(){}
}
}
위의 왼쪽과 같이 newmethod()라는 추상 메소드를 추가하는 대신 오른쪽과 같이 default 메소드를 추가하면, 기존의 MyInterface를 구현한 클래스를 변경하지 않아도 된다.
즉, 조상 클래스에 새로운 메소드를 추가한 것과 동일해지는 것이다. 대신 새로 추가된 default 메소드가 기존의 메소드와 이름이 중복되어 충돌하는 경우가 발생한다. 이 충돌을 해결하는 규칙은 다음과 같다.
1. 여러 인터페이스의 default 메소드 간의 충돌
- 인터페이스를 구현한 클래스에서 default 메소드를 오버라이딩 해야 한다.
2. default 메소드와 조상 클래스의 메소드 간의 충돌
- 조상 클래스의 메소드가 상속되고, 디폴트 메소드는 무시된다.
예제
(1)
class DefaultMethodTest {
public static void main(String[] args) {
Child c = new Child();
c.method1();
c.method2();
MyInterface.staticMethod();
MyInterface2.staticMethod();
}
}
class Child extends Parent implements MyInterface, MyInterface2 {
public void method1() {
System.out.println("method1() in Child"); // 오버라이딩
}
}
class Parent {
public void method2() {
System.out.println("method2() in Parent");
}
}
interface MyInterface {
default void method1() {
System.out.println("method1() in MyInterface");
}
default void method2() {
System.out.println("method2() in MyInterface");
}
static void staticMethod() {
System.out.println("staticMethod() in MyInterface");
}
}
interface MyInterface2 {
default void method1() {
System.out.println("method1() in MyInterface2");
}
static void staticMethod() {
System.out.println("staticMethod() in MyInterface2");
}
}
- 실행결과
method1() in Child
method2() in Parent
staticMethod() in MyInterface
staticMethod() in MyInterface2
(2)
import java.util.Scanner;
import java.util.InputMismatchException;
class ExceptionCase5 {
public static void main(String[] args) {
Scanner kb = new Scanner(System.in);
try {
System.out.print("a/b... a? ");
int n1 = kb.nextInt();
System.out.print("a/b... b? ");
int n2 = kb.nextInt();
System.out.printf("%d / %d = %d \n", n1, n2, n1/n2);
}
catch(ArithmeticException e) {
e.getMessage();
}
catch(InputMismatchException e) {
// 클래스 Scanner를 통한 값의 입력에서의 오류 상황을 의미하는 예외 클래스
e.getMessage();
}
System.out.println("Good bye~~!");
}
}
위의 소스코드의 catch 블록을 하나로 합치면
import java.util.Scanner;
import java.util.InputMismatchException;
class ExceptionCase6 {
public static void main(String[] args) {
Scanner kb = new Scanner(System.in);
try {
System.out.print("a/b... a? ");
int n1 = kb.nextInt();
System.out.print("a/b... b? ");
int n2 = kb.nextInt();
System.out.printf("%d / %d = %d \n", n1, n2, n1/n2);
}
catch(ArithmeticException | InputMismatchException e) {
e.getMessage();
}
System.out.println("Good bye~~!");
}
}
참고 예외처리는 성능의 저하로 이어진다.
try 구문 안에 위치한 코드는 try 구문 밖에 위치한 코드에 비해 실행 속도가 느리다. 따라서 과도한 예외처리는 심각한 성능의 저하로 이어질 수 있다.
따라서 예외처리가 불필요한 코드를 try구문 안에 두는 일을 자제해야 한다. 참고로 모든 예외를 민감하게 처리하는 프로그램은 생각보다 많지 않다. 규모가 클수록, 성능이 중요시될수록 try~catch문 이외의 다양한 방법으로 그리고 선별적으로 예외를 처리한다.
'Java > Day24' 카테고리의 다른 글
[Java] 이클립스 단축키 모음 (0) | 2021.12.05 |
---|---|
[Java] Vector 클래스 (0) | 2021.12.05 |
[Java] import문 & static import문 (0) | 2021.12.05 |
[Java] 예외처리 연습문제 (0) | 2021.12.05 |