상위 클래스로서 상속을 주기 위한 클래스로, 스스로 객체로 선언해서 인스턴스화 할 수 없고
하위 클래스에서 그 추상 클래스를 상속을 받아 그 안의 메서드나 필드를 사용하게끔 하는 클래스이다
어느정도의 틀을 제공하며 조금더 나아가서는 인터페이스의 내용과 연결되는 내용이다
abstract class Test1 {
int num1 = 10;
int num2 = 20;
public void method1() {
System.out.println("method1");
}
}
public class Ex01 {
public static void main(String[] args) {
// Cannot instantiate the type Test1
// 추상 메서드는 객체를 만들 수 없다!!
// Test1 t1 = new Test1();
}
}
abstract class Test1 {
int num1 = 10;
int num2 = 20;
public void method1() {
System.out.println("method1");
}
public abstract void method2();// {
// 추상 메서드는 함수 내용을 가질 수 없다
//}
}
class Test2 extends Test1{
// The type Test2 must implement the inherited abstract method Test1.method2()
// Test2는 상속받은 추상 메서드 Test1.method2()를 구현해야만 합니다!
}
그래서, 두가지의 방법이 있는데
1. 추상 메서드는 추상 클래스가 가질 수 있으므로 Test2를 추상 클래스화를 하거나
abstract class Test2 extends Test1{
}
class Test2 extends Test1{
@Override
public void method2() {
// TODO Auto-generated method stub
}
}
또 다른 특징으로는
abstract class A{
abstract void method5();
}
class B extends A {
@Override
void method5() {
System.out.println("나는 메서드 B");
}
}
class C extends A {
@Override
void method5() {
System.out.println("나는 메서드 C");
}
}
public class Practice2 {
public static void main(String[] args) {
A ob1 = new B();
A ob2 = new C();
ob1.method5();
ob2.method5();
}
}
추상클래스의 한 종류이다
interface Test3 {
// 인터페이스의 모든 필드는 public static final 속성
public static final int NUM1 = 10;
// 인터페이스의 모든 메서드는 public abstract
public abstract void method3();
}
// 인터페이스는 extends 대신 implements로 상속처리한다
class Test4 implements Test3 {
@Override
public void method3() {
System.out.println("method3");
}
}
public class Ex02 {
public static void main(String[] args) {
Test4 ob = new Test4();
ob.method3();
}
}
추상클래스를 객체로 생성하려면 추상메서드의 미구현 내용을 구현해야한다
매번 클래스를 만들어서 미구현 내용을 처리하고자 하면 가독성이 떨어지므로
이름없는 클래스를 즉석에서 만들어서 객체를 생성한다
이런 형식을 익명 내부 클래스(Anonymous Inner Class/ Type) 이라고 한다
자 그럼, 직접 익명 클래스를 만들면서 알아보자
1) 추상 클래스를 상속받아서 추상 메서드를 오버라이딩 하는 새로운 클래스를 작성한다
package abstractClass;
class Button {
String text;
OnClickListner listener;
public Button(String text) {
this.text = text;
}
void click() { // 클릭하면
if(listener != null) { // 클릭했을 때 동작이 지정되어 있다면
listener.onClick(); // 지정된 내용을 수행한다
}
else {
System.err.printf("%s : 클릭에 연결된 동작(기능, 함수)이 없습니다\n", text);
}
}
void setOnClickListener(OnClickListner listener) {
this.listener = listener;
}
public String toString() {
return String.format("[%s]", text);
}
}
abstract class OnClickListner {
abstract void onClick();
}
// 1) 추상 클래스를 상속받아서 추상 메서드를 오버라이딩 하는 새로운 클래스를 작성한다
class HelloWorld extends OnClickListner {
@Override
void onClick() {
System.out.println("Hello, world !!");
}
}
public class Ex03 {
public static void main(String[] args) {
Button btn1 = new Button("Hello");
// 2) 1번에서 만든 클래스를 활용하여 객체를 생성한다
HelloWorld listener1 = new HelloWorld();
// 3) 2번에서 생성한 객체를 버튼에 전달한다 (1번만 쓰고 더이상 사용되지 않는 클래스)
btn1.setOnClickListener(listener1);
System.out.println(btn1);
btn1.click();
Button btn2 = new Button("그냥 버튼");
btn2.click();
// Hello, World 가 아닌 다른 텍스트를 출력하거나, 다른 기능을 구현하려면
// 매번 새로운 클래스를 작성하여, OnClickListner를 상속받고, 메서드를 오버라이딩 한 이후
// 객체를 생성하여 버튼에 연결시켜주어야 한다
Button btn3 = new Button("JAVA");
// 객체를 생성하면서, 즉석에서 함수 내용만 지정하면 미구현 내용이 사라지므로, 객체를 생성할 수 있다
OnClickListner listener3 = new OnClickListner() {
@Override
void onClick() {
System.out.println("자바는 수다스럽다");
}
};
btn3.setOnClickListener(listener3);
btn3.click();
Button btn4 = new Button("Python");
btn4.setOnClickListener(new OnClickListner() {
@Override
void onClick() {
System.out.println("Life is too short, you need python");
}
});
btn4.click();
// 함수의 객체화
// 자바의 기본단위는 클래스이다. 그래서 자바에서 함수는 변수에 저장할 수 없다
// (자바에서 함수는 일급객체가 아니다)
// 함수를 전달하려면, 함수를 포함하는 객체를 생성해서 전달해야만 한다
// 함수의 내용을 자유롭게 작성하려면, 추상 메서드로 형식을 지정해두어야 한다
// 추상메서드로 형식이 지정된 함수를 오버라이딩하여 내용을 작성하고, 그 함수를 포함하는 객체를 전달한다
}
}
class PC {
// PC는 USB타입의 장치를 연결할 수 있다
void connect(USB device) {
device.onConnect(); // 연결된 장치마다, 연결되었을 때 수행하는 동작이 있다
}
// 만약 인터페이스를 사용하지 않았다면... 연결하고 싶은 장치의 개수만큼 함수 오버로딩을 수행해야 한다
// void connect(Keyboard device) { ... }
// void connect(DataCable device) { ... }
// void connect(ElectricFan device) { ... }
}
interface USB {
/*public abstract*/ void onConnect();
}
class Keyboard implements USB {
@Override
public void onConnect() {
System.out.println("키보드가 연결되었습니다");
}
}
class DataCable implements USB {
@Override
public void onConnect() {
System.out.println("데이터를 전송할 준비가 되었습니다");
}
}
class ElectricFan implements USB {
@Override
public void onConnect() {
System.out.println("선풍기 충전중...27%");
}
}
public class Ex04 {
public static void main(String[] args) {
PC pc = new PC();
Keyboard d1 = new Keyboard();
DataCable d2 = new DataCable();
ElectricFan d3 = new ElectricFan();
USB[] arr = { d1, d2, d3 }; // 다형성
for(int i = 0; i < arr.length; i++) {
pc.connect(arr[i]);
}
// 서로 다른 타입의 객체를 하나의 타입으로 묶어 줄 수 있다
// USB 타입이 되기 위해서는 반드시 추상메서드를 오버라이딩 해야 한다
// USB타입이면 onConnect() 함수가 있어서, PC에 connect했을때 저마다의 동작이 수행된다
// 인터페이스가 가지는 추상메서드가 하나만 있다면, 함수형 인터페이스로 분류한다
// 함수형 인터페이스는 람다식으로 객체를 생성할 수 있다
// 내부 구조는 익명 클래스와 다르다 (사용은 비슷하게 할 수 있다)
USB mouse = () -> System.out.println("마우스 연결됨...");
pc.connect(mouse);
// 람다식 기본 : () -> {}
// () 에는 함수의 매개변수를 넣어준다
// -> 는 고정으로 사용한다
// {} 에는 함수의 내용을 작성한다
// 함수가 하나밖에 없으니, 함수의 이름을 지정할 필요가 없다
// 만약, 매개변수가 하나라면 ()를 생략할 수 있다
// 만약, 함수의 실행 내용이 한줄이라면 {}를 생략할 수 있다
// 만약, 함수가 특정 타입을 반환하고, 실행내용이 한줄이라면 return을 생략할 수 있다
// 인터페이스도 넓은 범주에서 보면 추상 클래스이므로, 익명클래스 문법을 사용할 수 있다
USB mic = new USB() {
@Override
public void onConnect() {
System.out.println("마이크 연결됨...");
}
};
pc.connect(mic);
}
}